diff --git a/modules/demux/mp4/essetup.c b/modules/demux/mp4/essetup.c index 19b5ff16fc641d93c6b715ca27da24fb8e5a2e9b..a1be66f556149fc1712aba3692502be677f94394 100644 --- a/modules/demux/mp4/essetup.c +++ b/modules/demux/mp4/essetup.c @@ -280,13 +280,25 @@ int SetupVideoES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample ) p_track->fmt.video.orientation = ORIENT_ROTATED_90; break; case 180: - p_track->fmt.video.orientation = ORIENT_ROTATED_180; + if (p_track->i_flip == 1) { + p_track->fmt.video.orientation = ORIENT_VFLIPPED; + } else { + p_track->fmt.video.orientation = ORIENT_ROTATED_180; + } break; case 270: p_track->fmt.video.orientation = ORIENT_ROTATED_270; break; } + /* Flip, unless already flipped */ + if (p_track->i_flip == 1 && (int)p_track->f_rotation != 180) { + video_transform_t transform = (video_transform_t)p_track->fmt.video.orientation; + /* Flip first then rotate */ + p_track->fmt.video.orientation = ORIENT_HFLIPPED; + video_format_TransformBy(&p_track->fmt.video, transform); + } + /* Set 360 video mode */ p_track->fmt.video.projection_mode = PROJECTION_MODE_RECTANGULAR; const MP4_Box_t *p_uuid = MP4_BoxGet( p_track->p_track, "uuid" ); diff --git a/modules/demux/mp4/libmp4.c b/modules/demux/mp4/libmp4.c index cf9eeadf42fd6a37b9277be0deb59f986c2b8d5c..0f5e91fd40c329f7c3dfafaaa0b7903d8f46ef68 100644 --- a/modules/demux/mp4/libmp4.c +++ b/modules/demux/mp4/libmp4.c @@ -1227,6 +1227,15 @@ static int MP4_ReadBox_tkhd( stream_t *p_stream, MP4_Box_t *p_box ) double scale[2]; // scale factor; sx = scale[0] , sy = scale[1] int32_t *matrix = p_box->data.p_tkhd->i_matrix; + int64_t det = (int64_t)matrix[0] * matrix[4] - (int64_t)matrix[1] * matrix[3]; + if (det < 0) { + /* If determinant is negative copy the matrix and flip it horizontally. */ + const int flip[] = { -1, 1, 1 }; + for (int j = 0; j < 9; j++) + matrix[j] *= flip[j % 3]; + p_box->data.p_tkhd->i_flip = 1; + } + scale[0] = sqrt(conv_fx(matrix[0]) * conv_fx(matrix[0]) + conv_fx(matrix[3]) * conv_fx(matrix[3])); scale[1] = sqrt(conv_fx(matrix[1]) * conv_fx(matrix[1]) + diff --git a/modules/demux/mp4/libmp4.h b/modules/demux/mp4/libmp4.h index bfab16515f70a7da8bbef2f59f859037aafca338..dd3082d66ae1bce9a3a7d9c06c4d615b34e17b76 100644 --- a/modules/demux/mp4/libmp4.h +++ b/modules/demux/mp4/libmp4.h @@ -509,6 +509,7 @@ typedef struct MP4_Box_data_tkhd_s int32_t i_width; int32_t i_height; float f_rotation; + int i_flip; } MP4_Box_data_tkhd_t; diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c index a5178d860f2dd815944f937a578aeb8d0aa5be38..908890183bf94fdf9aaae97d811330136cd0d5cd 100644 --- a/modules/demux/mp4/mp4.c +++ b/modules/demux/mp4/mp4.c @@ -3367,6 +3367,7 @@ static void MP4_TrackSetup( demux_t *p_demux, mp4_track_t *p_track, p_track->i_width = BOXDATA(p_tkhd)->i_width / BLOCK16x16; p_track->i_height = BOXDATA(p_tkhd)->i_height / BLOCK16x16; p_track->f_rotation = BOXDATA(p_tkhd)->f_rotation; + p_track->i_flip = BOXDATA(p_tkhd)->i_flip; /* FIXME: unhandled box: tref */ diff --git a/modules/demux/mp4/mp4.h b/modules/demux/mp4/mp4.h index b7c6cdf3d718834fbe0ba98bd7f8d3843f0cd83c..d2855246a32477c3f9dfb209e840efcfd5af598a 100644 --- a/modules/demux/mp4/mp4.h +++ b/modules/demux/mp4/mp4.h @@ -99,6 +99,7 @@ typedef struct int i_width; int i_height; float f_rotation; + int i_flip; /* more internal data */ uint32_t i_timescale; /* time scale for this track only */ diff --git a/src/misc/es_format.c b/src/misc/es_format.c index c2a09132d1b3c8cd91834955065476a46c476287..63f5584ef02dccc79bf730cc2edd3a6ce699edd4 100644 --- a/src/misc/es_format.c +++ b/src/misc/es_format.c @@ -142,6 +142,7 @@ void video_format_Setup( video_format_t *p_fmt, vlc_fourcc_t i_chroma, p_fmt->i_visible_height = i_visible_height; p_fmt->i_x_offset = p_fmt->i_y_offset = 0; + p_fmt->orientation = ORIENT_NORMAL; vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den, i_sar_num, i_sar_den, 0 ); diff --git a/test/Makefile.am b/test/Makefile.am index c2b0d082c9b0ae8dfc4b3d213730e33dfce16be1..c9eaa59d2b6d76ec430f947fc4e59a85423050a8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -36,6 +36,15 @@ check_PROGRAMS = \ if ENABLE_SOUT check_PROGRAMS += test_modules_tls endif + +if HAVE_GL +check_PROGRAMS += test_modules_video_output_opengl_filters +endif + +if HAVE_GLES2 +check_PROGRAMS += test_modules_video_output_opengl_es2_filters +endif + if UPDATE_CHECK check_PROGRAMS += test_src_crypto_update endif @@ -130,6 +139,72 @@ test_modules_keystore_SOURCES = modules/keystore/test.c test_modules_keystore_LDADD = $(LIBVLCCORE) $(LIBVLC) test_modules_tls_SOURCES = modules/misc/tls.c test_modules_tls_LDADD = $(LIBVLCCORE) $(LIBVLC) +<<<<<<< HEAD +======= +test_modules_demux_timestamps_filter_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_modules_demux_timestamps_filter_SOURCES = modules/demux/timestamps_filter.c +test_modules_demux_ts_pes_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_modules_demux_ts_pes_SOURCES = modules/demux/ts_pes.c \ + ../modules/demux/mpeg/ts_pes.c \ + ../modules/demux/mpeg/ts_pes.h +test_modules_playlist_m3u_SOURCES = modules/demux/playlist/m3u.c +test_modules_playlist_m3u_LDADD = $(LIBVLCCORE) $(LIBVLC) + +test_modules_codec_hxxx_helper_SOURCES = modules/codec/hxxx_helper.c \ + ../modules/codec/hxxx_helper.c \ + ../modules/packetizer/hxxx_nal.c \ + ../modules/packetizer/h264_slice.c \ + ../modules/packetizer/h264_nal.c \ + ../modules/packetizer/hevc_nal.c +test_modules_codec_hxxx_helper_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_modules_video_output_opengl_filters_SOURCES = \ + modules/video_output/opengl/filters.c \ + ../modules/video_output/opengl/filters.c \ + ../modules/video_output/opengl/filters.h \ + ../modules/video_output/opengl/filter.c \ + ../modules/video_output/opengl/gl_api.c \ + ../modules/video_output/opengl/gl_api.h \ + ../modules/video_output/opengl/interop.c \ + ../modules/video_output/opengl/interop.h \ + ../modules/video_output/opengl/importer.c \ + ../modules/video_output/opengl/importer.h +test_modules_video_output_opengl_filters_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_modules_video_output_opengl_filters_CPPFLAGS = $(AM_CPPFLAGS) -DVLC_TEST_OPENGL_API=VLC_OPENGL +test_modules_video_output_opengl_es2_filters_SOURCES = $(test_modules_video_output_opengl_filters_SOURCES) +test_modules_video_output_opengl_es2_filters_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_modules_video_output_opengl_es2_filters_CPPFLAGS = $(AM_CPPFLAGS) -DVLC_TEST_OPENGL_API=VLC_OPENGL_ES2 + + +test_src_video_output_SOURCES = \ + src/video_output/video_output.c \ + src/video_output/video_output.h \ + src/video_output/video_output_scenarios.c +test_src_video_output_LDADD = $(LIBVLCCORE) $(LIBVLC) +test_src_video_output_opengl_SOURCES = src/video_output/opengl.c +test_src_video_output_opengl_LDADD = $(LIBVLCCORE) $(LIBVLC) + +test_modules_stream_out_transcode_SOURCES = \ + modules/stream_out/transcode.c \ + modules/stream_out/transcode.h \ + modules/stream_out/transcode_scenarios.c +test_modules_stream_out_transcode_LDADD = $(LIBVLCCORE) $(LIBVLC) + +test_modules_stream_out_pcr_sync_SOURCES = modules/stream_out/pcr_sync.c \ + ../modules/stream_out/transcode/pcr_sync.c \ + ../modules/stream_out/transcode/pcr_sync.h \ + ../modules/stream_out/transcode/pcr_helper.h \ + ../modules/stream_out/transcode/pcr_helper.c +test_modules_stream_out_pcr_sync_LDADD = $(LIBVLCCORE) + +test_src_input_decoder_SOURCES = \ + src/input/decoder/input_decoder.c \ + src/input/decoder/input_decoder.h \ + src/input/decoder/input_decoder_scenarios.c +test_src_input_decoder_LDADD = $(LIBVLCCORE) $(LIBVLC) + +test_src_misc_image_SOURCES = src/misc/image.c +test_src_misc_image_LDADD = $(LIBVLCCORE) $(LIBVLC) +>>>>>>> 8ae846de79 (test: opengl: add test for filters) checkall: $(MAKE) check_PROGRAMS="$(check_PROGRAMS) $(EXTRA_PROGRAMS)" check diff --git a/test/modules/video_output/opengl/filters.c b/test/modules/video_output/opengl/filters.c new file mode 100644 index 0000000000000000000000000000000000000000..4cf04cbd0ac55d61241cd0377645896e94d6f0c0 --- /dev/null +++ b/test/modules/video_output/opengl/filters.c @@ -0,0 +1,333 @@ +/***************************************************************************** + * opengl.c: test for the OpenGL client code + ***************************************************************************** + * Copyright (C) 2023 VideoLabs + * + * Authors: Alexandre Janniaux <ajanni@videolabs.io> + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef VLC_TEST_OPENGL_API +# error "Define VLC_TEST_OPENGL_API to the VLC_OPENGL API to use" +#endif + +/* Define a builtin module for mocked parts */ +#define MODULE_NAME test_opengl +#undef VLC_DYNAMIC_PLUGIN + +#include "../../../libvlc/test.h" +#include "../../../lib/libvlc_internal.h" +#include <vlc_common.h> +#include <vlc_plugin.h> +#include <vlc_codec.h> +#include <vlc_opengl.h> +#include <vlc_filter.h> +#include <vlc_modules.h> +#include <vlc_vout_display.h> + +#include "../../../modules/video_output/opengl/filters.h" +#include "../../../modules/video_output/opengl/gl_api.h" + +static_assert( + VLC_TEST_OPENGL_API == VLC_OPENGL || + VLC_TEST_OPENGL_API == VLC_OPENGL_ES2); + +const char vlc_module_name[] = MODULE_STRING; +static const uint8_t green[] = { 0x0, 0xff, 0x00, 0xff }; +static const uint8_t red[] = { 0xff, 0x0, 0x0, 0xff }; +static const uint8_t blue[] = { 0x00, 0x0, 0xff, 0xff }; + +struct test_point +{ + int x, y; + const uint8_t *color; +}; + +static void test_opengl_offscreen( + vlc_object_t *root, + video_orientation_t orientation, + struct test_point *points, + size_t points_count +){ + struct vlc_decoder_device *device = + vlc_decoder_device_Create(root, NULL); + vlc_gl_t *gl = vlc_gl_CreateOffscreen( + root, device, 3, 3, VLC_TEST_OPENGL_API, NULL, NULL); + assert(gl != NULL); + if (device != NULL) + vlc_decoder_device_Release(device); + + int ret = vlc_gl_MakeCurrent(gl); + assert(ret == VLC_SUCCESS); + + struct vlc_gl_api api; + ret = vlc_gl_api_Init(&api, gl); + assert(ret == VLC_SUCCESS); + + GLuint out_tex; + api.vt.GenTextures(1, &out_tex); + api.vt.BindTexture(GL_TEXTURE_2D, out_tex); + api.vt.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + api.vt.BindTexture(GL_TEXTURE_2D, 0); + + GLuint out_fb; + api.vt.GenFramebuffers(1, &out_fb); + api.vt.BindFramebuffer(GL_FRAMEBUFFER, out_fb); + api.vt.BindFramebuffer(GL_READ_FRAMEBUFFER, out_fb); + api.vt.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, out_tex, 0); + + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + GL_ASSERT_NOERROR(&api.vt); + + video_format_t fmt; + video_format_Setup(&fmt, VLC_CODEC_RGBA, 3, 3, 3, 3, 1, 1); + fmt.primaries = COLOR_PRIMARIES_SRGB; + fmt.space = COLOR_SPACE_SRGB; + fmt.transfer = TRANSFER_FUNC_SRGB; + fmt.projection_mode = PROJECTION_MODE_RECTANGULAR; + + struct vlc_gl_interop *interop = + vlc_gl_interop_New(gl, NULL, &fmt); + assert(interop != NULL); + GL_ASSERT_NOERROR(&api.vt); + + GLint fbo_binding; + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + struct vlc_gl_filters *filters = + vlc_gl_filters_New(gl, &api, interop, orientation); + assert(filters != NULL); + GL_ASSERT_NOERROR(&api.vt); + + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + struct vlc_gl_filter *filter = + vlc_gl_filters_Append(filters, "renderer", NULL); + assert(filter != NULL); + GL_ASSERT_NOERROR(&api.vt); + assert((GLuint)fbo_binding == out_fb); + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + (void)filter; + + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + ret = vlc_gl_filters_InitFramebuffers(filters); + assert(ret == VLC_SUCCESS); + GL_ASSERT_NOERROR(&api.vt); + + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + assert(api.vt.CheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + vlc_gl_filters_SetViewport(filters, 0, 0, 3, 3); + GL_ASSERT_NOERROR(&api.vt); + + vlc_gl_filters_SetOutputSize(filters, 3, 3); + GL_ASSERT_NOERROR(&api.vt); + + picture_t *picture = picture_NewFromFormat(&fmt); + assert(picture != NULL); + + size_t size = picture->p[0].i_lines * picture->p[0].i_pitch / picture->p[0].i_pixel_pitch; + for (size_t i=0; i < size; ++i) + memcpy(&picture->p[0].p_pixels[i * 4], green, sizeof(green)); + + memcpy(&picture->p[0].p_pixels[0 * 4], red, sizeof(red)); + memcpy(&picture->p[0].p_pixels[2 * 4], blue, sizeof(blue)); + + ret = vlc_gl_filters_UpdatePicture(filters, picture); + assert(ret == VLC_SUCCESS); + GL_ASSERT_NOERROR(&api.vt); + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + + ret = vlc_gl_filters_Draw(filters); + assert(ret == VLC_SUCCESS); + GL_ASSERT_NOERROR(&api.vt); + api.vt.GetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo_binding); + assert((GLuint)fbo_binding == out_fb); + + api.vt.Finish(); + GL_ASSERT_NOERROR(&api.vt); + + /* Disable pixel packing to use glReadPixels. */ + api.vt.BindBuffer(GL_PIXEL_PACK_BUFFER, 0); + GL_ASSERT_NOERROR(&api.vt); + + uint8_t pixel[4]; + + fprintf(stderr, "Results (vertically flipped):\n"); + for (size_t i = 0; i < 3; ++i) + { + for (size_t j = 0; j < 3; ++j) + { + api.vt.ReadPixels(j, i, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + fprintf(stderr, " %u:%u:%u:%u", pixel[0], pixel[1], pixel[2], pixel[3]); + } + fprintf(stderr, "\n"); + } + + for (size_t i = 0; i < points_count; ++i) + { + api.vt.ReadPixels(points[i].x, points[i].y, 1, 1, GL_RGBA, + GL_UNSIGNED_BYTE, &pixel); + GL_ASSERT_NOERROR(&api.vt); + assert(memcmp(pixel, points[i].color, sizeof(pixel)) == 0); + } + + vlc_gl_filters_Delete(filters); + GL_ASSERT_NOERROR(&api.vt); + + vlc_gl_interop_Delete(interop); + GL_ASSERT_NOERROR(&api.vt); + + vlc_gl_ReleaseCurrent(gl); + vlc_gl_Delete(gl); +} + +int main( int argc, char **argv ) +{ + (void)argc; (void)argv; + test_init(); + + const char * const vlc_argv[] = { + "-vvv", "--aout=dummy", "--text-renderer=dummy", + }; + + libvlc_instance_t *vlc = libvlc_new(ARRAY_SIZE(vlc_argv), vlc_argv); + vlc_object_t *root = &vlc->p_libvlc_int->obj; + + const char *cap = + (VLC_TEST_OPENGL_API == VLC_OPENGL) ? "opengl offscreen" : + (VLC_TEST_OPENGL_API == VLC_OPENGL_ES2) ? "opengl es2 offscreen" : + NULL; + assert(cap != NULL); + + fprintf(stderr, "Looking for cap %s\n", cap); + + module_t **providers; + size_t strict_matches; + ssize_t provider_available = vlc_module_match( + cap, NULL, false, &providers, &strict_matches); + (void)strict_matches; + free(providers); + + if (provider_available <= 0) + { + libvlc_release(vlc); + return 77; + } + + struct vlc_decoder_device *device = + vlc_decoder_device_Create(root, NULL); + vlc_gl_t *gl = vlc_gl_CreateOffscreen( + root, device, 3, 3, VLC_TEST_OPENGL_API, NULL, NULL); + if (device != NULL) + vlc_decoder_device_Release(device); + + if (gl == NULL) + { + libvlc_release(vlc); + return 77; + } + vlc_gl_Delete(gl); + + struct test_point points_normal[] = { + { 1, 1, green }, + { 0, 2, red }, + { 2, 2, blue }, + }; + test_opengl_offscreen(root, ORIENT_NORMAL, + points_normal, ARRAY_SIZE(points_normal)); + + struct test_point points_rotated_90[] = { + { 1, 1, green }, + { 0, 0, red }, + { 0, 2, blue }, + }; + test_opengl_offscreen(root, ORIENT_ROTATED_90, + points_rotated_90, ARRAY_SIZE(points_rotated_90)); + + struct test_point points_rotated_180[] = { + { 1, 1, green }, + { 2, 0, red }, + { 0, 0, blue }, + }; + test_opengl_offscreen(root, ORIENT_ROTATED_180, + points_rotated_180, ARRAY_SIZE(points_rotated_180)); + + struct test_point points_rotated_270[] = { + { 1, 1, green }, + { 2, 2, red }, + { 2, 0, blue }, + }; + test_opengl_offscreen(root, ORIENT_ROTATED_270, + points_rotated_270, ARRAY_SIZE(points_rotated_270)); + + struct test_point points_hflip[] = { + { 1, 1, green }, + { 2, 2, red }, + { 0, 2, blue }, + }; + test_opengl_offscreen(root, ORIENT_HFLIPPED, + points_hflip, ARRAY_SIZE(points_hflip)); + + struct test_point points_vflip[] = { + { 1, 1, green }, + { 0, 0, red }, + { 2, 0, blue }, + }; + test_opengl_offscreen(root, ORIENT_VFLIPPED, + points_vflip, ARRAY_SIZE(points_vflip)); + + struct test_point points_transposed[] = { + { 1, 1, green }, + { 2, 0, red }, + { 2, 2, blue }, + }; + test_opengl_offscreen(root, ORIENT_TRANSPOSED, + points_transposed, ARRAY_SIZE(points_transposed)); + + struct test_point points_antitransposed[] = { + { 1, 1, green }, + { 0, 2, red }, + { 0, 0, blue }, + }; + test_opengl_offscreen(root, ORIENT_ANTI_TRANSPOSED, + points_antitransposed, ARRAY_SIZE(points_antitransposed)); + + + + libvlc_release(vlc); + return 0; +}