Cobrand / mpv-rs

safe mpv bindings for Rust
Apache License 2.0
33 stars 9 forks source link

Bottom pixel row is black in SDL2 example #2

Open Boscop opened 7 years ago

Boscop commented 7 years ago

The bottom row of pixels is black in the SDL2 example. Maybe the whole video is shifted up by 1 px for some reason?

In the simple example it looks correct. [Link to test video]

Btw, the simple example uses 14% CPU while SDL2 example only uses 0.87% CPU, why?

Is it also possible to render video with glutin/glium with as low CPU usage as SDL2?

Cobrand commented 7 years ago

Please do separate issues next time, that will allow closing/searching them more easily in the future.

>>> 854 / 480
1.7791666666666666
>>> 960 / 540
1.7777777777777777

I suspect that difference creates a black line. at the bottom. Try resizing the window, (the playback should resize as well), and you will see the black line at the bottom eventually disappears depending on how you stretch the window.

Hope that helps.

Boscop commented 7 years ago

Thanks, yes, that was the reason for the black pixel row.. Now, this seems to work for glium (I'm getting 3% CPU usage with a Full HD video):

extern crate mpv;
#[macro_use] extern crate log;
#[macro_use] extern crate glium;

use glium::glutin;
use glutin::{Context, GlContext, KeyboardInput, VirtualKeyCode, get_primary_monitor};

use std::env;
use std::path::Path;
use std::os::raw::{c_void,c_char};
use std::ffi::CStr;

unsafe extern "C" fn get_proc_address(arg: *mut c_void, name: *const c_char) -> *mut c_void {
    let arg: &Context = *(arg as *mut &Context);
    let name = CStr::from_ptr(name).to_str().unwrap();
    arg.get_proc_address(name) as *mut c_void
}

fn glium_example(video_path: &Path) {
    let mut events_loop = glutin::EventsLoop::new();
    let window = glutin::WindowBuilder::new()
        .with_title("glium")
        .with_dimensions(1024, 768)
        // .with_fullscreen(get_primary_monitor())
    ;
    let context = glutin::ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();
    let win = display.gl_window();
    let ctx: &Context = win.context();
    let ptr = Box::into_raw(Box::new(ctx)) as *mut c_void;
    let mut mpv_builder = mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder");
    let _ = mpv_builder.try_hardware_decoding();
    let mut mpv: Box<mpv::MpvHandlerWithGl> = mpv_builder.build_with_gl(Some(get_proc_address), ptr).expect("Error while initializing MPV with opengl");

    // observe the property "pause" with userdata 5.
    // When we will pause later, an event PropertyChange will be sent with userdata 5
    mpv.observe_property::<bool>("pause",5).unwrap();

    let video_path = video_path.to_str().expect("Expected a string for Path, got None");

    // Send a command synchronously, telling the libmpv core to load a file
    mpv.command(&["loadfile", video_path as &str])
       .expect("Error loading file");

    let mut running = true;
    'main: while running {
        events_loop.poll_events(|event| match event {
            glutin::Event::WindowEvent{ event, .. } => match event {
                glutin::WindowEvent::KeyboardInput {
                    input: KeyboardInput {
                        virtual_keycode: Some(VirtualKeyCode::Escape), ..
                    }, ..
                } |
                glutin::WindowEvent::Closed => running = false,
                _ => ()
            },
            _ => ()
        });
        // wait up to 0.0 seconds for an event.
        while let Some(event) = mpv.wait_event(0.0) {
            // even if you don't do anything with the events, it is still necessary to empty
            // the event loop
            println!("RECEIVED EVENT: {:?}", event);
            match event {
                mpv::Event::Shutdown | mpv::Event::EndFile(_) => {
                    break 'main;
                }
                _ => {}
            };
        }
        let (width, height) = display.get_framebuffer_dimensions();
        mpv.draw(0, width as i32, -(height as i32)).expect("Failed to draw on SDL2 window");
        let _ = display.swap_buffers();
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        println!("Usage: ./sdl [any mp4, avi, mkv, ... file]");
    } else {
        let path: &Path = Path::new(&args[1]);
        if path.is_file() {
            glium_example(path);
        } else {
            println!("A file is required; {} is not a valid file", path.to_str().unwrap());
        }
    }
}

Is everything correct? It feels very hacky, because I'm getting a &Contex and boxing it and then casting it to a mutable pointer. Is there a better way, maybe to get a mutable Context directly somehow?

If this is correct, we can add the other stuff from the SDL2 example (pausing etc) and add this example to this repo for people who want to use glium.

As for drawing something over it, have you tried rendering the video to a FBO (SimpleFramebuffer), and then rendering that to the screen, followed by your overlay? Some people in #mpv recommended that, so that's what I want to try next, but I can't find a way to get the id of the SimpleFramebuffer to pass to mpv.draw(). Do you know how that can be done?

Cobrand commented 7 years ago

I can't help you further than that, one of the reasons I settled with sdl2 for the example here is that my knowledge of OpenGL is pretty limited, and sdl2 doesn't make it a must to use OpenGL with mpv.

For your first problem, you can just cast a & into a *const , then into a *mut _, at least you won't have memory leaks with this.

Boscop commented 7 years ago

But Context is a trait so that would be a fat pointer and that wouldn't be safe, right?

Cobrand commented 7 years ago

https://botbot.me/mozilla/rust/, look at 3:18am, August 14th, I've got the answer for you. TL;DR: Casting to a const Trait gives you the fat pointer, but casting to a const () afterwards gives you the thin pointer.