{"action":"created","comment":{"url":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/28412590","id":28412590,"diff_hunk":"@@ -0,0 +1,509 @@\n+/*\n+ * video output driver for libdrm\n+ *\n+ * by rr- <rr-@sakuya.pl>\n+ *\n+ * This file is part of mpv.\n+ *\n+ * mpv is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; either version 2 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * mpv is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with mpv.  If not, see <http://www.gnu.org/licenses/>.\n+ */\n+\n+#include <assert.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <stdbool.h>\n+#include <libswscale/swscale.h>\n+#include <sys/mman.h>\n+#include <xf86drm.h>\n+#include <xf86drmMode.h>\n+\n+#include \"common/msg.h\"\n+#include \"sub/osd.h\"\n+#include \"video/fmt-conversion.h\"\n+#include \"video/mp_image.h\"\n+#include \"video/sws_utils.h\"\n+#include \"vo.h\"\n+\n+#define BUF_COUNT 2\n+\n+struct modeset_buf {\n+    uint32_t width;\n+    uint32_t height;\n+    uint32_t stride;\n+    uint32_t size;\n+    uint32_t handle;\n+    uint8_t *map;\n+    uint32_t fb;\n+};\n+\n+struct modeset_dev {\n+    struct modeset_buf bufs[BUF_COUNT];\n+    drmModeModeInfo mode;\n+    uint32_t conn;\n+    uint32_t crtc;\n+    int front_buf;\n+};\n+\n+struct priv {\n+    int fd;\n+    struct modeset_dev *dev;\n+    drmModeCrtc *old_crtc;\n+\n+    char *device_path;\n+    int connector_id;\n+\n+    int32_t device_w;\n+    int32_t device_h;\n+    int32_t x, y;\n+    struct mp_image *curframe;\n+    struct mp_rect src;\n+    struct mp_rect dst;\n+    struct mp_osd_res osd;\n+    struct mp_sws_context *sws;\n+};\n+\n+static int modeset_open(struct vo *vo, int *out, const char *node)\n+{\n+    int fd;\n+    uint64_t has_dumb;\n+    fd = open(node, O_RDWR | O_CLOEXEC);\n+    if (fd < 0) {\n+        MP_ERR(vo, \"Cannot open \\\"%s\\\": %s.\\n\", node, mp_strerror(errno));\n+        return -errno;\n+    }\n+    if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) {\n+        MP_ERR(vo, \"Device \\\"%s\\\" does not support dumb buffers.\\n\", node);\n+        return -EOPNOTSUPP;\n+    }\n+    *out = fd;\n+    return 0;\n+}\n+\n+static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf)\n+{\n+    struct drm_mode_create_dumb creq;\n+    struct drm_mode_destroy_dumb dreq;\n+    struct drm_mode_map_dumb mreq;\n+    int ret;\n+\n+    // create dumb buffer\n+    memset(&creq, 0, sizeof(creq));\n+    creq.width = buf->width;\n+    creq.height = buf->height;\n+    creq.bpp = 32;\n+    ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);\n+    if (ret < 0) {\n+        MP_ERR(vo, \"Cannot create dumb buffer: %s\\n\", mp_strerror(errno));\n+        return -errno;\n+    }\n+    buf->stride = creq.pitch;\n+    buf->size = creq.size;\n+    buf->handle = creq.handle;\n+\n+    // create framebuffer object for the dumb-buffer\n+    ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride,\n+                       buf->handle, &buf->fb);\n+    if (ret) {\n+        MP_ERR(vo, \"Cannot create framebuffer: %s\\n\", mp_strerror(errno));\n+        ret = -errno;\n+        goto err_destroy;\n+    }\n+\n+    // prepare buffer for memory mapping\n+    memset(&mreq, 0, sizeof(mreq));\n+    mreq.handle = buf->handle;\n+    ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);\n+    if (ret) {\n+        MP_ERR(vo, \"Cannot map dumb buffer: %s\\n\", mp_strerror(errno));\n+        ret = -errno;\n+        goto err_fb;\n+    }\n+\n+    // perform actual memory mapping\n+    buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,\n+                    fd, mreq.offset);\n+    if (buf->map == MAP_FAILED) {\n+        MP_ERR(vo, \"Cannot map dumb buffer: %s\\n\", mp_strerror(errno));\n+        ret = -errno;\n+        goto err_fb;\n+    }\n+\n+    memset(buf->map, 0, buf->size);\n+\n+    return 0;\n+\n+err_fb:\n+    drmModeRmFB(fd, buf->fb);\n+err_destroy:\n+    memset(&dreq, 0, sizeof(dreq));\n+    dreq.handle = buf->handle;\n+    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);\n+    return ret;\n+}\n+\n+static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res,\n+                             drmModeConnector *conn, struct modeset_dev *dev)\n+{\n+    drmModeEncoder *enc;\n+    unsigned int i, j;\n+\n+    for (i = 0; i < conn->count_encoders; ++i) {\n+        enc = drmModeGetEncoder(fd, conn->encoders[i]);\n+        if (!enc) {\n+            MP_WARN(vo, \"Cannot retrieve encoder %u:%u: %s\\n\",\n+                    i, conn->encoders[i], mp_strerror(errno));\n+            continue;\n+        }\n+\n+        // iterate all global CRTCs\n+        for (j = 0; j < res->count_crtcs; ++j) {\n+            // check whether this CRTC works with the encoder\n+            if (!(enc->possible_crtcs & (1 << j)))\n+                continue;\n+\n+            drmModeFreeEncoder(enc);\n+            dev->crtc = res->crtcs[j];\n+            return 0;\n+        }\n+\n+        drmModeFreeEncoder(enc);\n+    }\n+\n+    MP_ERR(vo, \"Connector %u has no suitable CRTC\\n\", conn->connector_id);\n+    return -ENOENT;\n+}\n+\n+static void modeset_destroy_fb(int fd, struct modeset_buf *buf)\n+{\n+    struct drm_mode_destroy_dumb dreq;\n+\n+    munmap(buf->map, buf->size);\n+\n+    drmModeRmFB(fd, buf->fb);\n+\n+    memset(&dreq, 0, sizeof(dreq));\n+    dreq.handle = buf->handle;\n+    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);\n+}\n+\n+static bool is_connector_valid(struct vo *vo, int conn_id,\n+                               drmModeConnector *conn, bool silent)\n+{\n+    if (!conn) {\n+        if (!silent) {\n+            MP_ERR(vo, \"Cannot get connector %d: %s\\n\", conn_id,\n+                   mp_strerror(errno));\n+        }\n+        return false;\n+    }\n+\n+    if (conn->connection != DRM_MODE_CONNECTED) {\n+        if (!silent) {\n+            MP_ERR(vo, \"Connector %d is disconnected\\n\", conn_id);\n+        }\n+        return false;\n+    }\n+\n+    if (conn->count_modes == 0) {\n+        if (!silent) {\n+            MP_ERR(vo, \"Connector %d has no valid modes\\n\", conn_id);\n+        }\n+        return false;\n+    }\n+\n+    return true;\n+}\n+\n+static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id,\n+                               struct modeset_dev **out)\n+{\n+    struct modeset_dev *dev = NULL;\n+    drmModeConnector *conn = NULL;\n+    drmModeRes *res = NULL;\n+    int ret = 0;\n+\n+    *out = NULL;\n+\n+    res = drmModeGetResources(fd);\n+    if (!res) {\n+        MP_ERR(vo, \"Cannot retrieve DRM resources: %s\\n\", mp_strerror(errno));\n+        ret = -errno;\n+        goto end;\n+    }\n+\n+    if (conn_id == -1) {\n+        // get the first connected connector\n+        for (int i = 0; i < res->count_connectors; i ++) {\n+            conn = drmModeGetConnector(fd, res->connectors[i]);\n+            if (is_connector_valid(vo, i, conn, true)) {\n+                conn_id = i;\n+                break;\n+            }\n+            if (conn) {\n+                drmModeFreeConnector(conn);\n+                conn = NULL;\n+            }\n+        }\n+        if (conn_id == -1) {\n+            MP_ERR(vo, \"No connected connectors found\\n\");\n+            ret = -ENODEV;\n+            goto end;\n+        }\n+    }\n+\n+    if (conn_id < 0 || conn_id >= res->count_connectors) {\n+        MP_ERR(vo, \"Bad connector ID. Max valid connector ID = %u\\n\",\n+               res->count_connectors);\n+        ret = -ENODEV;\n+        goto end;\n+    }\n+\n+    conn = drmModeGetConnector(fd, res->connectors[conn_id]);\n+    if (!is_connector_valid(vo, conn_id, conn, true)) {\n+        ret = -ENODEV;\n+        goto end;\n+    }\n+\n+    dev = talloc_zero(vo->priv, struct modeset_dev);\n+    dev->conn = conn->connector_id;\n+    dev->front_buf = 0;\n+    dev->mode = conn->modes[0];\n+    dev->bufs[0].width = conn->modes[0].hdisplay;\n+    dev->bufs[0].height = conn->modes[0].vdisplay;\n+    dev->bufs[1].width = conn->modes[0].hdisplay;\n+    dev->bufs[1].height = conn->modes[0].vdisplay;\n+\n+    MP_INFO(vo, \"Connector using mode %ux%u\\n\",\n+            dev->bufs[0].width, dev->bufs[0].height);\n+\n+    ret = modeset_find_crtc(vo, fd, res, conn, dev);\n+    if (ret) {\n+        MP_ERR(vo, \"Connector %d has no valid CRTC\\n\", conn_id);\n+        goto end;\n+    }\n+\n+    unsigned int i, j;\n+    for (i = 0; i < BUF_COUNT; i ++) {\n+        ret = modeset_create_fb(vo, fd, &dev->bufs[i]);\n+        if (ret) {\n+            for (j = 0; j < i; j ++) {\n+                modeset_destroy_fb(fd, &dev->bufs[j]);\n+            }\n+            MP_ERR(vo, \"Cannot create framebuffer for connector %d\\n\",\n+                   conn_id);\n+            return ret;\n+        }\n+    }\n+\n+end:\n+    if (conn) {\n+        drmModeFreeConnector(conn);\n+        conn = NULL;\n+    }\n+    if (res) {\n+        drmModeFreeResources(res);\n+        res = NULL;\n+    }\n+    if (ret == 0) {\n+        *out = dev;\n+    } else {\n+        talloc_free(dev);\n+    }\n+    return ret;\n+}\n+\n+\n+\n+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)\n+{\n+    struct priv *p = vo->priv;\n+\n+    vo->dwidth = p->device_w;\n+    vo->dheight = p->device_h;\n+    vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);\n+\n+    int32_t w = p->dst.x1 - p->dst.x0;\n+    int32_t h = p->dst.y1 - p->dst.y0;\n+\n+    // p->osd contains the parameters assuming OSD rendering in window\n+    // coordinates, but OSD can only be rendered in the intersection\n+    // between window and video rectangle (i.e. not into panscan borders).\n+    p->osd.w = w;\n+    p->osd.h = h;\n+    p->osd.mt = MPMIN(0, p->osd.mt);\n+    p->osd.mb = MPMIN(0, p->osd.mb);\n+    p->osd.mr = MPMIN(0, p->osd.mr);\n+    p->osd.ml = MPMIN(0, p->osd.ml);\n+\n+    p->x = (p->device_w - w) >> 1;\n+    p->y = (p->device_h - h) >> 1;\n+\n+    mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts);\n+    p->sws->src = *params;\n+    p->sws->dst = (struct mp_image_params) {\n+        .imgfmt = IMGFMT_BGR0,\n+        .w = w,\n+        .h = h,\n+        .d_w = w,\n+        .d_h = h,\n+    };\n+\n+    if (p->curframe) talloc_free(p->curframe);\n+    p->curframe = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h);\n+    mp_image_params_guess_csp(&p->sws->dst);\n+    mp_image_set_params(p->curframe, &p->sws->dst);\n+\n+    if (mp_sws_reinit(p->sws) < 0)\n+        return 1;\n+\n+    vo->want_redraw = true;\n+    return 0;\n+}\n+\n+static void draw_image(struct vo *vo, mp_image_t *mpi)\n+{\n+    struct priv *p = vo->priv;\n+\n+    struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];\n+\n+    struct mp_rect src_rc = p->src;\n+    src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);\n+    src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);\n+    mp_image_crop_rc(mpi, src_rc);\n+\n+    mp_sws_scale(p->sws, p->curframe, mpi);\n+\n+    osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->curframe);\n+\n+    int32_t shift = (p->device_w * p->y + p->x) * 4;\n+    memcpy_pic(front_buf->map + shift,\n+               p->curframe->planes[0],\n+               (p->dst.x1 - p->dst.x0) * 4,\n+               p->device_h,\n+               p->device_w * 4,\n+               p->curframe->stride[0]);\n+\n+    talloc_free(mpi);\n+}\n+\n+static void flip_page(struct vo *vo)\n+{\n+    struct priv *p = vo->priv;\n+\n+    int ret = drmModeSetCrtc(p->fd, p->dev->crtc,\n+                             p->dev->bufs[p->dev->front_buf].fb,\n+                             0, 0, &p->dev->conn, 1, &p->dev->mode);\n+    if (ret) {\n+        MP_WARN(vo, \"Cannot flip page for connector\\n\");\n+    } else {\n+        p->dev->front_buf ++;\n+        p->dev->front_buf %= BUF_COUNT;\n+    }\n+}\n+\n+static int preinit(struct vo *vo)\n+{\n+    struct priv *p = vo->priv;\n+    p->dev = NULL;\n+    p->fd = 0;\n+    p->old_crtc = NULL;\n+    p->curframe = NULL;\n+    p->sws = mp_sws_alloc(vo);\n+\n+    int ret;\n+\n+    ret = modeset_open(vo, &p->fd, p->device_path);\n+    if (ret)\n+        return ret;\n+\n+    ret = modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev);\n+    if (ret)\n+        return ret;\n+\n+    assert(p->dev);\n+    p->device_w = p->dev->bufs[0].width;\n+    p->device_h = p->dev->bufs[0].height;\n+\n+    p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);\n+    ret = drmModeSetCrtc(p->fd, p->dev->crtc,\n+                         p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,\n+                         0, 0, &p->dev->conn, 1, &p->dev->mode);\n+    if (ret) {\n+        MP_ERR(vo, \"Cannot set CRTC for connector %u: %s\\n\", p->connector_id,\n+               mp_strerror(errno));\n+    }\n+\n+    return 0;\n+}\n+\n+static void uninit(struct vo *vo)\n+{\n+    struct priv *p = vo->priv;\n+\n+    if (p->dev) {\n+        if (p->old_crtc) {\n+            drmModeSetCrtc(p->fd,\n+                           p->old_crtc->crtc_id,\n+                           p->old_crtc->buffer_id,\n+                           p->old_crtc->x,\n+                           p->old_crtc->y,\n+                           &p->dev->conn,\n+                           1,\n+                           &p->dev->mode);\n+            drmModeFreeCrtc(p->old_crtc);\n+        }\n+\n+        modeset_destroy_fb(p->fd, &p->dev->bufs[1]);\n+        modeset_destroy_fb(p->fd, &p->dev->bufs[0]);\n+    }\n+\n+    talloc_free(p->curframe);\n+    talloc_free(p->dev);\n+}\n+\n+static int query_format(struct vo *vo, int format)\n+{\n+    if (sws_isSupportedInput(imgfmt2pixfmt(format)))\n+        return 1;\n+    return format == IMGFMT_BGR0;","path":"video/out/vo_drm.c","position":479,"original_position":479,"commit_id":"5665a8ad3bf2858b55071854e4736d6bd65f4220","original_commit_id":"5665a8ad3bf2858b55071854e4736d6bd65f4220","user":{"login":"wm4","id":1387750,"avatar_url":"https://avatars.githubusercontent.com/u/1387750?v=3","gravatar_id":"","url":"https://api.github.com/users/wm4","html_url":"https://github.com/wm4","followers_url":"https://api.github.com/users/wm4/followers","following_url":"https://api.github.com/users/wm4/following{/other_user}","gists_url":"https://api.github.com/users/wm4/gists{/gist_id}","starred_url":"https://api.github.com/users/wm4/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wm4/subscriptions","organizations_url":"https://api.github.com/users/wm4/orgs","repos_url":"https://api.github.com/users/wm4/repos","events_url":"https://api.github.com/users/wm4/events{/privacy}","received_events_url":"https://api.github.com/users/wm4/received_events","type":"User","site_admin":false},"body":"This is redundant now.","created_at":"2015-04-15T11:48:27Z","updated_at":"2015-04-15T11:48:27Z","html_url":"https://github.com/mpv-player/mpv/pull/1811#discussion_r28412590","pull_request_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1811","_links":{"self":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/28412590"},"html":{"href":"https://github.com/mpv-player/mpv/pull/1811#discussion_r28412590"},"pull_request":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1811"}}},"pull_request":{"url":"https://api.github.com/repos/mpv-player/mpv/pulls/1811","id":33176487,"html_url":"https://github.com/mpv-player/mpv/pull/1811","diff_url":"https://github.com/mpv-player/mpv/pull/1811.diff","patch_url":"https://github.com/mpv-player/mpv/pull/1811.patch","issue_url":"https://api.github.com/repos/mpv-player/mpv/issues/1811","number":1811,"state":"open","locked":false,"title":"vo_drm: add KMS/DRM renderer support","user":{"login":"rr-","id":1045476,"avatar_url":"https://avatars.githubusercontent.com/u/1045476?v=3","gravatar_id":"","url":"https://api.github.com/users/rr-","html_url":"https://github.com/rr-","followers_url":"https://api.github.com/users/rr-/followers","following_url":"https://api.github.com/users/rr-/following{/other_user}","gists_url":"https://api.github.com/users/rr-/gists{/gist_id}","starred_url":"https://api.github.com/users/rr-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rr-/subscriptions","organizations_url":"https://api.github.com/users/rr-/orgs","repos_url":"https://api.github.com/users/rr-/repos","events_url":"https://api.github.com/users/rr-/events{/privacy}","received_events_url":"https://api.github.com/users/rr-/received_events","type":"User","site_admin":false},"body":"This pull request introduces new video output, `vo_drm`. It uses Direct Rendering Manager (DRM) and Kernel Mode Setting (KMS) to enable watching videos on Linux systems, where neither X window system nor wayland server are available. Its only dependency is `libdrm` (it doesn't even need OpenGL) and thus I draw a bold conclusion that in terms of dependencies, it is the most lightweight video renderer for Linux.\r\n\r\nBecause of this, it doesn't use hardware acceleration at all, and heavy videos / old hardware *will* experience performance problems. This was tested on a 5-year-old netbook equipped with AMD C-50 (1gHz) and Radeon 6250. Old movies were played acceptably, while 720p action videos with rich ass subtitles experienced massive frame drops. This behavior could be fixed to some extent with extreme `--vf=scale=-1:180 --sws-scaler=point`.\r\n\r\nI have no idea how it performs on better hardware. I'm going to purchase a powerful computer within one month.\r\n\r\nAdditionally, I (eventually) plan to develop a renderer for DRM that utilizes hardware acceleration, but it will need `libegl` and almost surely `libgbm` that come with their own (remarkable) chain of dependencies. In my opinion, these dependencies are what makes `vo_drm` attractive.","created_at":"2015-04-13T15:51:34Z","updated_at":"2015-04-15T11:48:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4eb625c7cac0a19922349cce8606eeb608526b0d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1811/commits","review_comments_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1811/comments","review_comment_url":"https://api.github.com/repos/mpv-player/mpv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mpv-player/mpv/issues/1811/comments","statuses_url":"https://api.github.com/repos/mpv-player/mpv/statuses/5665a8ad3bf2858b55071854e4736d6bd65f4220","head":{"label":"rr-:drm","ref":"drm","sha":"5665a8ad3bf2858b55071854e4736d6bd65f4220","user":{"login":"rr-","id":1045476,"avatar_url":"https://avatars.githubusercontent.com/u/1045476?v=3","gravatar_id":"","url":"https://api.github.com/users/rr-","html_url":"https://github.com/rr-","followers_url":"https://api.github.com/users/rr-/followers","following_url":"https://api.github.com/users/rr-/following{/other_user}","gists_url":"https://api.github.com/users/rr-/gists{/gist_id}","starred_url":"https://api.github.com/users/rr-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rr-/subscriptions","organizations_url":"https://api.github.com/users/rr-/orgs","repos_url":"https://api.github.com/users/rr-/repos","events_url":"https://api.github.com/users/rr-/events{/privacy}","received_events_url":"https://api.github.com/users/rr-/received_events","type":"User","site_admin":false},"repo":{"id":33629723,"name":"mpv","full_name":"rr-/mpv","owner":{"login":"rr-","id":1045476,"avatar_url":"https://avatars.githubusercontent.com/u/1045476?v=3","gravatar_id":"","url":"https://api.github.com/users/rr-","html_url":"https://github.com/rr-","followers_url":"https://api.github.com/users/rr-/followers","following_url":"https://api.github.com/users/rr-/following{/other_user}","gists_url":"https://api.github.com/users/rr-/gists{/gist_id}","starred_url":"https://api.github.com/users/rr-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rr-/subscriptions","organizations_url":"https://api.github.com/users/rr-/orgs","repos_url":"https://api.github.com/users/rr-/repos","events_url":"https://api.github.com/users/rr-/events{/privacy}","received_events_url":"https://api.github.com/users/rr-/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rr-/mpv","description":"Video player based on MPlayer/mplayer2","fork":true,"url":"https://api.github.com/repos/rr-/mpv","forks_url":"https://api.github.com/repos/rr-/mpv/forks","keys_url":"https://api.github.com/repos/rr-/mpv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rr-/mpv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rr-/mpv/teams","hooks_url":"https://api.github.com/repos/rr-/mpv/hooks","issue_events_url":"https://api.github.com/repos/rr-/mpv/issues/events{/number}","events_url":"https://api.github.com/repos/rr-/mpv/events","assignees_url":"https://api.github.com/repos/rr-/mpv/assignees{/user}","branches_url":"https://api.github.com/repos/rr-/mpv/branches{/branch}","tags_url":"https://api.github.com/repos/rr-/mpv/tags","blobs_url":"https://api.github.com/repos/rr-/mpv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rr-/mpv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rr-/mpv/git/refs{/sha}","trees_url":"https://api.github.com/repos/rr-/mpv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rr-/mpv/statuses/{sha}","languages_url":"https://api.github.com/repos/rr-/mpv/languages","stargazers_url":"https://api.github.com/repos/rr-/mpv/stargazers","contributors_url":"https://api.github.com/repos/rr-/mpv/contributors","subscribers_url":"https://api.github.com/repos/rr-/mpv/subscribers","subscription_url":"https://api.github.com/repos/rr-/mpv/subscription","commits_url":"https://api.github.com/repos/rr-/mpv/commits{/sha}","git_commits_url":"https://api.github.com/repos/rr-/mpv/git/commits{/sha}","comments_url":"https://api.github.com/repos/rr-/mpv/comments{/number}","issue_comment_url":"https://api.github.com/repos/rr-/mpv/issues/comments{/number}","contents_url":"https://api.github.com/repos/rr-/mpv/contents/{+path}","compare_url":"https://api.github.com/repos/rr-/mpv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rr-/mpv/merges","archive_url":"https://api.github.com/repos/rr-/mpv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rr-/mpv/downloads","issues_url":"https://api.github.com/repos/rr-/mpv/issues{/number}","pulls_url":"https://api.github.com/repos/rr-/mpv/pulls{/number}","milestones_url":"https://api.github.com/repos/rr-/mpv/milestones{/number}","notifications_url":"https://api.github.com/repos/rr-/mpv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rr-/mpv/labels{/name}","releases_url":"https://api.github.com/repos/rr-/mpv/releases{/id}","created_at":"2015-04-08T20:11:57Z","updated_at":"2015-04-13T06:51:15Z","pushed_at":"2015-04-14T18:14:03Z","git_url":"git://github.com/rr-/mpv.git","ssh_url":"git@github.com:rr-/mpv.git","clone_url":"https://github.com/rr-/mpv.git","svn_url":"https://github.com/rr-/mpv","homepage":"http://mpv.io","size":67963,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mpv-player:master","ref":"master","sha":"3b2cc79d2f42eefebf4390ccf722e1bb02d6f734","user":{"login":"mpv-player","id":2550273,"avatar_url":"https://avatars.githubusercontent.com/u/2550273?v=3","gravatar_id":"","url":"https://api.github.com/users/mpv-player","html_url":"https://github.com/mpv-player","followers_url":"https://api.github.com/users/mpv-player/followers","following_url":"https://api.github.com/users/mpv-player/following{/other_user}","gists_url":"https://api.github.com/users/mpv-player/gists{/gist_id}","starred_url":"https://api.github.com/users/mpv-player/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpv-player/subscriptions","organizations_url":"https://api.github.com/users/mpv-player/orgs","repos_url":"https://api.github.com/users/mpv-player/repos","events_url":"https://api.github.com/users/mpv-player/events{/privacy}","received_events_url":"https://api.github.com/users/mpv-player/received_events","type":"Organization","site_admin":false},"repo":{"id":6201092,"name":"mpv","full_name":"mpv-player/mpv","owner":{"login":"mpv-player","id":2550273,"avatar_url":"https://avatars.githubusercontent.com/u/2550273?v=3","gravatar_id":"","url":"https://api.github.com/users/mpv-player","html_url":"https://github.com/mpv-player","followers_url":"https://api.github.com/users/mpv-player/followers","following_url":"https://api.github.com/users/mpv-player/following{/other_user}","gists_url":"https://api.github.com/users/mpv-player/gists{/gist_id}","starred_url":"https://api.github.com/users/mpv-player/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpv-player/subscriptions","organizations_url":"https://api.github.com/users/mpv-player/orgs","repos_url":"https://api.github.com/users/mpv-player/repos","events_url":"https://api.github.com/users/mpv-player/events{/privacy}","received_events_url":"https://api.github.com/users/mpv-player/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mpv-player/mpv","description":"Video player based on MPlayer/mplayer2","fork":false,"url":"https://api.github.com/repos/mpv-player/mpv","forks_url":"https://api.github.com/repos/mpv-player/mpv/forks","keys_url":"https://api.github.com/repos/mpv-player/mpv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mpv-player/mpv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mpv-player/mpv/teams","hooks_url":"https://api.github.com/repos/mpv-player/mpv/hooks","issue_events_url":"https://api.github.com/repos/mpv-player/mpv/issues/events{/number}","events_url":"https://api.github.com/repos/mpv-player/mpv/events","assignees_url":"https://api.github.com/repos/mpv-player/mpv/assignees{/user}","branches_url":"https://api.github.com/repos/mpv-player/mpv/branches{/branch}","tags_url":"https://api.github.com/repos/mpv-player/mpv/tags","blobs_url":"https://api.github.com/repos/mpv-player/mpv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mpv-player/mpv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mpv-player/mpv/git/refs{/sha}","trees_url":"https://api.github.com/repos/mpv-player/mpv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mpv-player/mpv/statuses/{sha}","languages_url":"https://api.github.com/repos/mpv-player/mpv/languages","stargazers_url":"https://api.github.com/repos/mpv-player/mpv/stargazers","contributors_url":"https://api.github.com/repos/mpv-player/mpv/contributors","subscribers_url":"https://api.github.com/repos/mpv-player/mpv/subscribers","subscription_url":"https://api.github.com/repos/mpv-player/mpv/subscription","commits_url":"https://api.github.com/repos/mpv-player/mpv/commits{/sha}","git_commits_url":"https://api.github.com/repos/mpv-player/mpv/git/commits{/sha}","comments_url":"https://api.github.com/repos/mpv-player/mpv/comments{/number}","issue_comment_url":"https://api.github.com/repos/mpv-player/mpv/issues/comments{/number}","contents_url":"https://api.github.com/repos/mpv-player/mpv/contents/{+path}","compare_url":"https://api.github.com/repos/mpv-player/mpv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mpv-player/mpv/merges","archive_url":"https://api.github.com/repos/mpv-player/mpv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mpv-player/mpv/downloads","issues_url":"https://api.github.com/repos/mpv-player/mpv/issues{/number}","pulls_url":"https://api.github.com/repos/mpv-player/mpv/pulls{/number}","milestones_url":"https://api.github.com/repos/mpv-player/mpv/milestones{/number}","notifications_url":"https://api.github.com/repos/mpv-player/mpv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mpv-player/mpv/labels{/name}","releases_url":"https://api.github.com/repos/mpv-player/mpv/releases{/id}","created_at":"2012-10-13T08:08:44Z","updated_at":"2015-04-15T11:29:00Z","pushed_at":"2015-04-15T11:29:00Z","git_url":"git://github.com/mpv-player/mpv.git","ssh_url":"git@github.com:mpv-player/mpv.git","clone_url":"https://github.com/mpv-player/mpv.git","svn_url":"https://github.com/mpv-player/mpv","homepage":"http://mpv.io","size":97834,"stargazers_count":1729,"watchers_count":1729,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":235,"mirror_url":null,"open_issues_count":200,"forks":235,"open_issues":200,"watchers":1729,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1811"},"html":{"href":"https://github.com/mpv-player/mpv/pull/1811"},"issue":{"href":"https://api.github.com/repos/mpv-player/mpv/issues/1811"},"comments":{"href":"https://api.github.com/repos/mpv-player/mpv/issues/1811/comments"},"review_comments":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1811/comments"},"review_comment":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1811/commits"},"statuses":{"href":"https://api.github.com/repos/mpv-player/mpv/statuses/5665a8ad3bf2858b55071854e4736d6bd65f4220"}}}}