1 /*
2 * Copyright (c) 2014 Lukasz Marek
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 //TODO: support for more formats
22 //TODO: support for more systems.
23 //TODO: implement X11, Windows, Mac OS native default window. SDL 1.2 doesn't allow to render to custom thread.
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stddef.h>
29
30 #include "config.h"
31
32 #if HAVE_WINDOWS_H
33 #include <windows.h>
34 #endif
35 #if HAVE_OPENGL_GL3_H
36 #include <OpenGL/gl3.h>
37 #elif HAVE_ES2_GL_H
38 #include <ES2/gl.h>
39 #else
40 #include <GL/gl.h>
41 #include <GL/glext.h>
42 #endif
43 #if HAVE_GLXGETPROCADDRESS
44 #include <GL/glx.h>
45 #endif
46
47 #if CONFIG_SDL2
48 #include <SDL.h>
49 #endif
50
62
63 #ifndef APIENTRY
65 #endif
66
67 /* FF_GL_RED_COMPONENT is used for planar pixel types.
68 * Only red component is sampled in shaders.
69 * On some platforms GL_RED is not available and GL_LUMINANCE have to be used,
70 * but since OpenGL 3.0 GL_LUMINANCE is deprecated.
71 * GL_RED produces RGBA = value, 0, 0, 1.
72 * GL_LUMINANCE produces RGBA = value, value, value, 1.
73 * Note: GL_INTENSITY may also be used which produce RGBA = value, value, value, value. */
74 #if defined(GL_RED)
75 #define FF_GL_RED_COMPONENT GL_RED
76 #elif defined(GL_LUMINANCE)
77 #define FF_GL_RED_COMPONENT GL_LUMINANCE
78 #else
79 #define FF_GL_RED_COMPONENT 0x1903; //GL_RED
80 #endif
81
82 /* Constants not defined for iOS */
83 #define FF_GL_UNSIGNED_BYTE_3_3_2 0x8032
84 #define FF_GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
85 #define FF_GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
86 #define FF_GL_UNPACK_ROW_LENGTH 0x0CF2
87
88 /* MinGW exposes only OpenGL 1.1 API */
89 #define FF_GL_ARRAY_BUFFER 0x8892
90 #define FF_GL_ELEMENT_ARRAY_BUFFER 0x8893
91 #define FF_GL_STATIC_DRAW 0x88E4
92 #define FF_GL_FRAGMENT_SHADER 0x8B30
93 #define FF_GL_VERTEX_SHADER 0x8B31
94 #define FF_GL_COMPILE_STATUS 0x8B81
95 #define FF_GL_LINK_STATUS 0x8B82
96 #define FF_GL_INFO_LOG_LENGTH 0x8B84
122
150
151 #define OPENGL_ERROR_CHECK(ctx) \
152 {\
153 GLenum err_code; \
154 if ((err_code = glGetError()) != GL_NO_ERROR) { \
155 av_log(ctx, AV_LOG_ERROR, "OpenGL error occurred in '%s', line %d: %d\n", __func__, __LINE__, err_code); \
156 goto fail; \
157 } \
158 }\
159
161 {
162 float x,
y,
z;
///<Position
163 float s0,
t0;
///<Texture coords
165
166 /* defines 2 triangles to display */
168 {
169 0, 1, 2,
170 0, 3, 2,
171 };
172
174 AVClass *
class;
///< class for private options
175
176 #if CONFIG_SDL2
178 SDL_GLContext glcontext;
179 #endif
181
182 int inited;
///< Set to 1 when write_header was successfully called.
186
187 /* OpenGL implementation limits */
193
194 /* Current OpenGL configuration */
210
216
217 /* Stream information */
227
230
279 };
280
284
286 {
295 }
300 }
304 }
308 }
309 }
310
312 {
321 goto end;
322 }
324 goto end;
326 }
327 end:
329 }
330
332 {
339 }
343 }
345 }
346
347 #if CONFIG_SDL2
349 {
352 SDL_Event event;
353 SDL_PumpEvents();
354 while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) > 0) {
355 switch (event.type) {
356 case SDL_QUIT:
358 case SDL_KEYDOWN:
359 switch (event.key.keysym.sym) {
360 case SDLK_ESCAPE:
361 case SDLK_q:
363 }
364 return 0;
365 case SDL_WINDOWEVENT:
366 switch(event.window.event) {
367 case SDL_WINDOWEVENT_RESIZED:
368 case SDL_WINDOWEVENT_SIZE_CHANGED:
369 SDL_GL_GetDrawableSize(opengl->window, &
message.width, &
message.height);
371 default:
372 break;
373 }
374 }
375 }
376 return 0;
377 }
378
380 {
383 if (SDL_Init(SDL_INIT_VIDEO)) {
386 }
388 SDL_WINDOWPOS_UNDEFINED,
389 SDL_WINDOWPOS_UNDEFINED,
391 SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
392 if (!opengl->window) {
393 av_log(opengl,
AV_LOG_ERROR,
"Unable to create default window: %s\n", SDL_GetError());
395 }
396 opengl->glcontext = SDL_GL_CreateContext(opengl->window);
397 if (!opengl->glcontext) {
398 av_log(opengl,
AV_LOG_ERROR,
"Unable to create OpenGL context on default window: %s\n", SDL_GetError());
400 }
401 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
402 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
403 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
404 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
405 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
407 SDL_GL_GetDrawableSize(opengl->window, &
message.width, &
message.height);
409 }
410
412 {
414
415 #define LOAD_OPENGL_FUN(name, type) \
416 procs->name = (type)SDL_GL_GetProcAddress(#name); \
417 if (!procs->name) { \
418 av_log(opengl, AV_LOG_ERROR, "Cannot load OpenGL function: '%s'\n", #name); \
419 return AVERROR(ENOSYS); \
420 }
421
447
448 return 0;
449
450 #undef LOAD_OPENGL_FUN
451 }
452 #endif /* CONFIG_SDL2 */
453
454 #if defined(__APPLE__)
456 {
458
459 #if CONFIG_SDL2
461 return opengl_sdl_load_procedures(opengl);
462 #endif
463
489 return 0;
490 }
491 #else
493 {
495
496 #if HAVE_GLXGETPROCADDRESS
497 #define SelectedGetProcAddress glXGetProcAddress
498 #elif HAVE_WGLGETPROCADDRESS
499 #define SelectedGetProcAddress wglGetProcAddress
500 #endif
501
502 #define LOAD_OPENGL_FUN(name, type) \
503 procs->name = (type)SelectedGetProcAddress(#name); \
504 if (!procs->name) { \
505 av_log(opengl, AV_LOG_ERROR, "Cannot load OpenGL function: '%s'\n", #name); \
506 return AVERROR(ENOSYS); \
507 }
508
509 #if CONFIG_SDL2
511 return opengl_sdl_load_procedures(opengl);
512 #endif
513
539
540 return 0;
541
542 #undef SelectedGetProcAddress
543 #undef LOAD_OPENGL_FUN
544 }
545 #endif
546
548 {
549 memset(
matrix, 0, 16 *
sizeof(
float));
551 }
552
554 float bottom, float top, float nearZ, float farZ)
555 {
556 float ral = right +
left;
557 float rsl = right -
left;
558 float tab = top + bottom;
559 float tsb = top - bottom;
560 float fan = farZ + nearZ;
561 float fsn = farZ - nearZ;
562
563 memset(
matrix, 0, 16 *
sizeof(
float));
571 }
572
574 {
576 static const struct{
577 const char *extension;
578 int major;
579 int minor;
580 } required_extensions[] = {
581 { "GL_ARB_multitexture", 1, 3 },
582 { "GL_ARB_vertex_buffer_object", 1, 5 }, //GLX_ARB_vertex_buffer_object
583 { "GL_ARB_vertex_shader", 2, 0 },
584 { "GL_ARB_fragment_shader", 2, 0 },
585 { "GL_ARB_shader_objects", 2, 0 },
587 };
589 const char *extensions, *
version;
590
591 version = glGetString(GL_VERSION);
592 extensions = glGetString(GL_EXTENSIONS);
596 }
597
599 if (sscanf(
version,
"%d.%d", &major, &minor) != 2)
601
602 for (
i = 0; required_extensions[
i].extension;
i++) {
603 if (major < required_extensions[
i].major &&
604 (major == required_extensions[
i].major && minor < required_extensions[
i].minor) &&
605 !strstr(extensions, required_extensions[
i].extension)) {
607 required_extensions[
i].extension);
610 }
611 }
614 opengl->
non_pow_2_textures = major >= 2 || strstr(extensions,
"GL_ARB_texture_non_power_of_two");
615 #if defined(GL_ES_VERSION_2_0)
616 opengl->
unpack_subimage = !!strstr(extensions,
"GL_EXT_unpack_subimage");
617 #else
619 #endif
620
626
628 return 0;
631 }
632
634 {
639 }
641 }
642
644 {
646 case GL_UNSIGNED_SHORT:
648 case GL_UNSIGNED_SHORT_5_6_5:
649 return 2;
650 case GL_UNSIGNED_BYTE:
653 default:
654 break;
655 }
656 return 1;
657 }
658
660 {
666 break;
667 }
668 }
669 }
670
672 {
673 AVRational sar, dar;
/* sample and display aspect ratios */
677
678 /* compute overlay width and height from the codec context information */
681
682 /* we suppose the screen has a 1/1 sample aspect ratio */
683 /* fit in the window */
685 /* fit in width */
688 } else {
689 /* fit in height */
692 }
693 }
694
696 int *out_width, int *out_height)
697 {
699 *out_width = in_width;
700 *out_height = in_height;
701 } else {
703 unsigned power_of_2 = 1;
704 while (power_of_2 <
max)
705 power_of_2 *= 2;
706 *out_height = power_of_2;
707 *out_width = power_of_2;
709 in_width, in_height, *out_width, *out_height);
710 }
711 }
712
714 {
718
719 /* We need order of components, not exact position, some minor HACKs here */
725
728 return;
729
730 #define FILL_COMPONENT(i) { \
731 shift = (desc->comp[i].depth - 1) >> 3; \
732 opengl->color_map[(i << 2) + (desc->comp[i].offset >> shift)] = 1.0; \
733 }
734
741
742 #undef FILL_COMPONENT
743 }
744
746 {
749 if (!shader) {
751 return 0;
752 }
755
765 }
766 }
768 }
770 return shader;
773 return 0;
774 }
775
777 {
780
781 if (!fragment_shader_code) {
785 }
786
792 }
794 fragment_shader_code);
798 }
799
803
807
819 }
821 }
822
834
836 return 0;
843 }
844
847 {
848 if (texture) {
849 int new_width, new_height;
851 glBindTexture(GL_TEXTURE_2D, texture);
852 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
853 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
854 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
855 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
856 glTexImage2D(GL_TEXTURE_2D, 0, opengl->
format, new_width, new_height, 0,
859 }
860 return 0;
863 }
864
866 {
868 int tex_w, tex_h;
869
874 }
879 1.0f, -1.0f);
881
883
889
891
900
905 return 0;
908 }
909
911 {
916 for (
i = 0;
i < 4;
i++)
921 }
928
930 return 0;
933 }
934
936 {
939
941 #if CONFIG_SDL2
942 if ((
ret = opengl_sdl_create_window(
h)) < 0) {
945 }
946 #else
947 av_log(opengl,
AV_LOG_ERROR,
"FFmpeg is compiled without SDL. Cannot create default window.\n");
949 #endif
950 } else {
959 }
963 }
964 }
965 return 0;
966 }
967
969 {
973 #if CONFIG_SDL2
974 SDL_GL_DeleteContext(opengl->glcontext);
975 SDL_DestroyWindow(opengl->window);
976 SDL_Quit();
977 #endif
981 }
982 return 0;
983 }
984
986 {
988
992
995
996 return 0;
997 }
998
1000 {
1003
1006
1010
1016 }
1017
1019 if (
desc->nb_components > 1) {
1021 int num_planes =
desc->nb_components - (has_alpha ? 1 : 0);
1025 } else {
1028 }
1029 for (
i = 1;
i < num_planes;
i++)
1034 else
1036 if (has_alpha)
1038 }
1039
1043
1044 glEnable(GL_BLEND);
1045 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1046
1048 (
float)opengl->
background[2] / 255.0f, 1.0f);
1049
1052
1053 return 0;
1056 }
1057
1059 {
1064
1067 "The opengl output device is deprecated due to being fundamentally incompatible with libavformat API. "
1068 "For monitoring purposes in ffmpeg you can output to a file or use pipes and a video player.\n"
1069 "Example: ffmpeg -i INPUT -f nut -c:v rawvideo - | ffplay -\n"
1070 );
1072 }
1073
1074 if (
h->nb_streams != 1 ||
1077 av_log(opengl,
AV_LOG_ERROR,
"Only a single raw or wrapped avframe video stream is supported.\n");
1079 }
1088
1091
1094
1097
1103 }
1104
1107
1110
1113
1116
1117 glClear(GL_COLOR_BUFFER_BIT);
1118
1119 #if CONFIG_SDL2
1121 SDL_GL_SwapWindow(opengl->window);
1122 #endif
1127 }
1128
1131
1133 return 0;
1134
1138 }
1139
1142 {
1147 int plane =
desc->comp[comp_index].plane;
1148
1149 switch(plane) {
1150 case 0:
1151 break;
1152 case 1:
1154 break;
1155 case 2:
1157 data += width_chroma * height_chroma * wordsize;
1158 break;
1159 case 3:
1161 data += 2 * width_chroma * height_chroma * wordsize;
1162 break;
1163 default:
1165 }
1167 }
1168
1169 #define LOAD_TEXTURE_DATA(comp_index, sub) \
1170 { \
1171 int width = sub ? AV_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w) : opengl->width; \
1172 int height = sub ? AV_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h): opengl->height; \
1173 uint8_t *data; \
1174 int plane = desc->comp[comp_index].plane; \
1175 \
1176 glBindTexture(GL_TEXTURE_2D, opengl->texture_name[comp_index]); \
1177 if (!is_pkt) { \
1178 GLint length = ((AVFrame *)input)->linesize[plane]; \
1179 int bytes_per_pixel = opengl_type_size(opengl->type); \
1180 if (!(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) \
1181 bytes_per_pixel *= desc->nb_components; \
1182 data = ((AVFrame *)input)->data[plane]; \
1183 if (!(length % bytes_per_pixel) && \
1184 (opengl->unpack_subimage || ((length / bytes_per_pixel) == width))) { \
1185 length /= bytes_per_pixel; \
1186 if (length != width) \
1187 glPixelStorei(FF_GL_UNPACK_ROW_LENGTH, length); \
1188 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, \
1189 opengl->format, opengl->type, data); \
1190 if (length != width) \
1191 glPixelStorei(FF_GL_UNPACK_ROW_LENGTH, 0); \
1192 } else { \
1193 int h; \
1194 for (h = 0; h < height; h++) { \
1195 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, h, width, 1, \
1196 opengl->format, opengl->type, data); \
1197 data += length; \
1198 } \
1199 } \
1200 } else { \
1201 data = opengl_get_plane_pointer(opengl, input, comp_index, desc); \
1202 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, \
1203 opengl->format, opengl->type, data); \
1204 } \
1205 }
1206
1208 {
1213
1214 #if CONFIG_SDL2
1215 /* At this point, opengl->glcontext implies opengl->glcontext */
1216 if (opengl->glcontext)
1217 SDL_GL_MakeCurrent(opengl->window, opengl->glcontext);
1218
1219 if (!opengl->
no_window && (
ret = opengl_sdl_process_events(
h)) < 0)
1221 #endif
1226 }
1227
1228 glClear(GL_COLOR_BUFFER_BIT);
1229
1230 if (!repaint) {
1231 if (is_pkt)
1232 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1239 }
1240 }
1243
1246
1253
1255
1258
1259 #if CONFIG_SDL2
1261 SDL_GL_SwapWindow(opengl->window);
1262 #endif
1267 }
1268
1269 return 0;
1272 }
1273
1275 {
1280 } else {
1282 }
1283 }
1284
1287 {
1289 return 0;
1291 }
1292
1293 #define OFFSET(x) offsetof(OpenGLContext, x)
1294 #define ENC AV_OPT_FLAG_ENCODING_PARAM
1301 };
1302
1309 };
1310
1324 };