Skip to content

Commit

Permalink
examples: Use new fps limiter in videoplayer example
Browse files Browse the repository at this point in the history
  • Loading branch information
thekovic authored and rasky committed Sep 8, 2024
1 parent ab8d510 commit a896d81
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 54 deletions.
2 changes: 1 addition & 1 deletion examples/videoplayer/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ filesystem/movie.m1v: $(MOVIE_FILE)
@mkdir -p "$(dir $@)"
if [ -s "$<" ]; then \
if command -v ffmpeg; then \
ffmpeg -y -i "$<" -vb 800K -vf 'scale=320x176' -r 20 "$@" ; \
ffmpeg -y -i "$<" -vb 800K -vf 'scale=288x160' -r 24 "$@" ; \
else \
echo "videoplayer: Skipping video re-encoding because ffmpeg is not installed" ; \
touch "$@" ; \
Expand Down
129 changes: 76 additions & 53 deletions examples/videoplayer/videoplayer.c
Original file line number Diff line number Diff line change
@@ -1,98 +1,121 @@
#include <libdragon.h>
#include "../../src/video/profile.h"

#define NUM_DISPLAY 8

void audio_poll(void) {
if (audio_can_write()) {
PROFILE_START(PS_AUDIO, 0);
short *buf = audio_write_begin();
mixer_poll(buf, audio_get_buffer_length());
audio_write_end();
PROFILE_STOP(PS_AUDIO, 0);
}
}

void video_poll(void) {


}

int main(void) {
// Number of frame back buffers we reserve.
// These buffers are used to render the video ahead of time.
// More buffers help ensure smooth video playback at the cost of more memory.
#define NUM_DISPLAY 8

// Maximum target audio frequency.
//
// Needs to be 48 kHz if Opus audio compression is used.
// In this example, we are using VADPCM audio compression
// which means we can use the real frequency of the audio track.
#define AUDIO_HZ 32000.0f

// Target screen resolution that we render at.
// Choosing a resolution above 240p (interlaced) can't be recommended for video playback.
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

int main(void)
{
joypad_init();
debug_init_isviewer();
debug_init_usblog();

display_init(RESOLUTION_320x240, DEPTH_32_BPP, NUM_DISPLAY, GAMMA_NONE, FILTERS_DISABLED);
display_init((resolution_t)
{
.width = SCREEN_WIDTH,
.height = SCREEN_HEIGHT,
.interlaced = INTERLACE_OFF
},
// 32-bit display mode is mandatory for video playback.
DEPTH_32_BPP,
NUM_DISPLAY, GAMMA_NONE,
// FILTERS_DISABLED disables all VI post-processing to achieve the sharpest
// possible image. If you'd like to soften the image a little bit, switch to
// FITLERS_RESAMPLE.
FILTERS_DISABLED);

dfs_init(DFS_DEFAULT_LOCATION);
rdpq_init();
profile_init();
yuv_init();

audio_init(44100, 4);
audio_init(AUDIO_HZ, 4);
mixer_init(8);

// Check if the movie is present in the filesystem, so that we can provide
// a specific error message
// a specific error message.
FILE *f = fopen("rom:/movie.m1v", "rb");
assertf(f, "Movie not found!\nInstall wget and ffmpeg to download and encode the sample movie\n");
fclose(f);

mpeg2_t *mp2 = mpeg2_open("rom:/movie.m1v");

// Create a YUV blitter to draw this movie
// Open the movie using the mpeg2 module and create a YUV blitter to draw it.
mpeg2_t* video_track = mpeg2_open("rom:/movie.m1v");
yuv_blitter_t yuv = yuv_blitter_new_fmv(
mpeg2_get_width(mp2), mpeg2_get_height(mp2), // Video size
display_get_width(), display_get_height(), // Output size
NULL // Colorspace (use default)
);

wav64_t music;
wav64_open(&music, "movie.wav64");

float fps = mpeg2_get_framerate(mp2);
throttle_init(fps, 0, 8);

mixer_ch_play(0, &music.wave);
// Resolution of the video we expect to play.
// Video needs to have a width divisible by 32 and a height divisible by 16.
//
// Here we have a video resolution of 288x160 which is a nice, valid resolution
// that leaves a margin of 32 pixels on the side - great for making sure
// CRT TVs with overscan still display the entire frame of your video.
// The resolution is not an exact 16:9 ratio (16:8.88) but it's close enough that
// most people won't notice. The lower resolution can also help with performance.
mpeg2_get_width(video_track), mpeg2_get_height(video_track),
// Set blitter's output area to our entire display
display_get_width(), display_get_height(),
// Override default FMV parms to not zoom the video.
// This will leave our desired CRT TV-friendly margin around the video.
&(yuv_fmv_parms_t) {.zoom = YUV_ZOOM_NONE}
);

// Engage the fps limiter to ensure proper video pacing.
float fps = mpeg2_get_framerate(video_track);
display_set_fps_limit(fps);

// Open the audio track and start playing it in channel 0.
wav64_t audio_track;
wav64_open(&audio_track, "movie.wav64");
mixer_ch_play(0, &audio_track.wave);

int nframes = 0;
display_context_t disp = 0;

while (1) {
mixer_throttle(44100.0f / fps);
while (1)
{
mixer_throttle(AUDIO_HZ / fps);

if (!mpeg2_next_frame(mp2))
if (!mpeg2_next_frame(video_track))
{
break;
}

disp = display_get();
// This polls the mixer to try and play the next chunk of audio, if available.
// We call this function twice during the frame to make sure the audio never stalls.
mixer_try_play();

// rdpq_attach(disp, NULL);
rdpq_attach_clear(disp, NULL);
rdpq_attach(display_get(), NULL);

PROFILE_START(PS_YUV, 0);
yuv_frame_t frame = mpeg2_get_frame(mp2);
// Get the next video frame and feed it into our previously set up blitter.
yuv_frame_t frame = mpeg2_get_frame(video_track);
yuv_blitter_run(&yuv, &frame);
PROFILE_STOP(PS_YUV, 0);

rdpq_detach_show();

audio_poll();

nframes++;

int ret = throttle_wait();
if (ret < 0) {
debugf("videoplayer: frame %d too slow (%d Kcycles)\n", nframes, -ret);
}

audio_poll();
mixer_try_play();

PROFILE_START(PS_SYNC, 0);
rspq_wait();
PROFILE_STOP(PS_SYNC, 0);

profile_next_frame();
if (nframes % 128 == 0) {
if (nframes % 128 == 0)
{
profile_dump();
profile_init();
}
Expand Down

0 comments on commit a896d81

Please sign in to comment.