Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283/* * This file is part of mpv. * Parts based on MPlayer code by Reimar Döffinger. * * 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 <stddef.h>#include <stdint.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <assert.h>
#include <libavutil/sha.h>#include <libavutil/intreadwrite.h>#include <libavutil/mem.h>
#include "osdep/io.h"
#include "common/common.h"#include "options/path.h"#include "stream/stream.h"#include "formats.h"#include "utils.h"
// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)static const char *gl_error_to_string(GLenum error){ switch (error) { case GL_INVALID_ENUM: return "INVALID_ENUM"; case GL_INVALID_VALUE: return "INVALID_VALUE"; case GL_INVALID_OPERATION: return "INVALID_OPERATION"; case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION"; case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; default: return "unknown"; }}
void gl_check_error(GL *gl, struct mp_log *log, const char *info){ for (;;) { GLenum error = gl->GetError(); if (error == GL_NO_ERROR) break; mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info, gl_error_to_string(error)); }}
static int get_alignment(int stride){ if (stride % 8 == 0) return 8; if (stride % 4 == 0) return 4; if (stride % 2 == 0) return 2; return 1;}
// upload a texture, handling things like stride and slices// target: texture target, usually GL_TEXTURE_2D// format, type: texture parameters// dataptr, stride: image data// x, y, width, height: part of the image to uploadvoid gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type, const void *dataptr, int stride, int x, int y, int w, int h){ int bpp = gl_bytes_per_pixel(format, type); const uint8_t *data = dataptr; int y_max = y + h; if (w <= 0 || h <= 0 || !bpp) return; assert(stride > 0); gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride)); int slice = h; if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) { // this is not always correct, but should work for MPlayer gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp); } else { if (stride != bpp * w) slice = 1; // very inefficient, but at least it works } for (; y + slice <= y_max; y += slice) { gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data); data += stride * slice; } if (y < y_max) gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);}
bool gl_read_fbo_contents(GL *gl, int fbo, int dir, GLenum format, GLenum type, int w, int h, uint8_t *dst, int dst_stride){ assert(dir == 1 || dir == -1); if (fbo == 0 && gl->es) return false; // ES can't read from front buffer gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT; gl->PixelStorei(GL_PACK_ALIGNMENT, 1); gl->ReadBuffer(obj); // reading by line allows flipping, and avoids stride-related trouble int y1 = dir > 0 ? 0 : h; for (int y = 0; y < h; y++) gl->ReadPixels(0, y, w, 1, format, type, dst + (y1 + dir * y) * dst_stride); gl->PixelStorei(GL_PACK_ALIGNMENT, 4); gl->BindFramebuffer(GL_FRAMEBUFFER, 0); return true;}
static void gl_vao_enable_attribs(struct gl_vao *vao){ GL *gl = vao->gl;
for (int n = 0; n < vao->num_entries; n++) { const struct ra_renderpass_input *e = &vao->entries[n]; GLenum type = 0; bool normalized = false; switch (e->type) { case RA_VARTYPE_INT: type = GL_INT; break; case RA_VARTYPE_FLOAT: type = GL_FLOAT; break; case RA_VARTYPE_BYTE_UNORM: type = GL_UNSIGNED_BYTE; normalized = true; break; default: abort(); } assert(e->dim_m == 1);
gl->EnableVertexAttribArray(n); gl->VertexAttribPointer(n, e->dim_v, type, normalized, vao->stride, (void *)(intptr_t)e->offset); }}
void gl_vao_init(struct gl_vao *vao, GL *gl, int stride, const struct ra_renderpass_input *entries, int num_entries){ assert(!vao->vao); assert(!vao->buffer);
*vao = (struct gl_vao){ .gl = gl, .stride = stride, .entries = entries, .num_entries = num_entries, };
gl->GenBuffers(1, &vao->buffer);
if (gl->BindVertexArray) { gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
gl->GenVertexArrays(1, &vao->vao); gl->BindVertexArray(vao->vao); gl_vao_enable_attribs(vao); gl->BindVertexArray(0);
gl->BindBuffer(GL_ARRAY_BUFFER, 0); }}
void gl_vao_uninit(struct gl_vao *vao){ GL *gl = vao->gl; if (!gl) return;
if (gl->DeleteVertexArrays) gl->DeleteVertexArrays(1, &vao->vao); gl->DeleteBuffers(1, &vao->buffer);
*vao = (struct gl_vao){0};}
static void gl_vao_bind(struct gl_vao *vao){ GL *gl = vao->gl;
if (gl->BindVertexArray) { gl->BindVertexArray(vao->vao); } else { gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer); gl_vao_enable_attribs(vao); gl->BindBuffer(GL_ARRAY_BUFFER, 0); }}
static void gl_vao_unbind(struct gl_vao *vao){ GL *gl = vao->gl;
if (gl->BindVertexArray) { gl->BindVertexArray(0); } else { for (int n = 0; n < vao->num_entries; n++) gl->DisableVertexAttribArray(n); }}
// Draw the vertex data (as described by the gl_vao_entry entries) in ptr// to the screen. num is the number of vertices. prim is usually GL_TRIANGLES.// If ptr is NULL, then skip the upload, and use the data uploaded with the// previous call.void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num){ GL *gl = vao->gl;
if (ptr) { gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer); gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW); gl->BindBuffer(GL_ARRAY_BUFFER, 0); }
gl_vao_bind(vao);
gl->DrawArrays(prim, 0, num);
gl_vao_unbind(vao);}
static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam){ // keep in mind that the debug callback can be asynchronous struct mp_log *log = (void *)userParam; int level = MSGL_ERR; switch (severity) { case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break; case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break; case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break; case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break; } mp_msg(log, level, "GL: %s\n", message);}
void gl_set_debug_logger(GL *gl, struct mp_log *log){ if (gl->DebugMessageCallback) gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);}
// Given a GL combined extension string in extensions, find out whether ext// is included in it. Basically, a word search.bool gl_check_extension(const char *extensions, const char *ext){ int len = strlen(ext); const char *cur = extensions; while (cur) { cur = strstr(cur, ext); if (!cur) break; if ((cur == extensions || cur[-1] == ' ') && (cur[len] == '\0' || cur[len] == ' ')) return true; cur += len; } return false;}