mpegg

package module
v0.0.0-...-b64afed Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2024 License: MIT Imports: 9 Imported by: 0

README

mpegg

Go Reference

NOTICE: Development is halted until some issues are resolved in gen2brain/mpeg. Don't recommend using this package yet.

A simple abstraction layer over gen2brain/mpeg to make it easier to play videos on Ebitengine. It uses MPEG-1, which is an old video format but kinda does the job at the expense of x2-x3 video sizes.

Code example

First, you need a video in MPEG-1 format. You can grab one somewhere or convert your own with ffmpeg:

ffmpeg -i my_video.mp4 -c:v mpeg1video -c:a mp2 -f mpeg -q 8 my_video.mpg

Then you write a simple program to play it on Ebitengine:

package main

import ( "os" ; "log" )
import "github.com/hajimehoshi/ebiten/v2"
import "github.com/hajimehoshi/ebiten/v2/audio"
import "github.com/tinne26/mpegg"

type VideoPlayer struct { player *mpegg.Player }
func (vp *VideoPlayer) Layout(w, h int) (int, int) { return w, h }
func (vp *VideoPlayer) Update() error { return nil }
func (vp *VideoPlayer) Draw(screen *ebiten.Image) {
	mpegg.Draw(screen, vp.player.CurrentFrame())
}

func main() {
	// initialize audio context (necessary if the video has audio)
	audio.NewContext(44100) // sample rate must match the video

	// open the video and create the mpeg player
	src, err := os.Open("my_video.mpg")
	if err != nil { log.Fatal(err) }
	mpeggPlayer, err := mpegg.NewPlayer(src)
	if err != nil { log.Fatal(err) }
	mpeggPlayer.Play() // activate the playback clock

	// run the video player!
	err = ebiten.RunGame(&VideoPlayer{ mpeggPlayer })
	if err != nil { log.Fatal(err) }
}

The mpegg.Player also provides easy access to the main mpeg properties and a few control functions:

  • Player.Play(), Player.Pause() and Player.IsPlaying() to play or pause the video.
  • Player.SeekFast(time.Duration), and Player.Position() time.Duration to control the playback position.
  • Player.MPEG() (*mpeg.MPEG, *sync.Mutex) to access the underlying mpeg.

Check the package's documentation to explore these and a few more.

Limitations

TODO: write about biggest sizes and framerates viable even for low-end laptop CPUs.

TODO: write about audio and video sync limitations on Ebitengine being ~10ms coarse.

ffmpeg cheatsheet

Here's a small list of useful ffmpeg flags. See the ffmpeg tips doc for the full commands and further info:

  • -v quiet -stats: prevent noisy command output but still show conversion progress.
  • -ss 3 -t 10: convert only a fragment of the video (start at second 3, convert 10 seconds of video).
  • -q 4: set the quality level explicitly. Lower is better.
  • -filter:v fps=VALUE: set framerate explicitly (better don't go below 24!).
  • -s 640x480: adjust video resolution.
  • -an instead of -c:a mp2: remove the audio to keep only the video.
  • ffprobe -i my_video.mpg -hide_banner: show video info if you have ffprobe.

License

The code is licensed under the MIT license.

Acknowledgements

This package is only a small wrapper, all credit goes to gen2brain/mpeg, based on the original phoboslab/pl_mpeg C library, and hajimehoshi/ebiten.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoVideo         = errors.New("mpeg doesn't include any video stream")
	ErrMissingHeaders  = errors.New("one or more mpeg streams are missing headers")
	ErrNilAudioContext = errors.New("mpeg has audio stream but audio.Context is not initialized")
	ErrBadSampleRate   = errors.New("mpeg audio stream and audio context sample rates don't match")
	ErrTooManyChannels = errors.New("mpeg audio streams with more than 2 channels are not supported")
)

A collection of initialization errors defined by this package for NewPlayer(). Other mpeg-specific errors are also possible.

Functions

func Draw

func Draw(viewport *ebiten.Image, frame *ebiten.Image)

A utility function to draw a frame into the given viewport, scaling as required with ebiten.FilterLinear to take as much space as possible while preserving the aspect ratio.

If there's extra space in the viewport, the frame will be drawn centered, but black bars won't be explicitly drawn, so whatever was on the background of the viewport will remain visible.

Common usage:

mpegg.Draw(screen, mpegPlayer.CurrentFrame())

Types

type Player

type Player struct {
	// contains filtered or unexported fields
}

A Player represents an mpeg video player, typically also including audio.

The player is a simple abstraction layer or wrapper around the lower level gen2brain/mpeg types, which implement the underlying decoders used to make playing video possible on Ebitengine.

Usage is quite similar to Ebitengine audio players:

More methods are available, but that's the main idea.

func NewPlayer

func NewPlayer(mpegVideoReader io.ReadSeeker) (*Player, error)

Creates a new mpeg video Player. The read-seeker source is usually a file opened with os.Open().

func (*Player) CurrentFrame

func (self *Player) CurrentFrame() *ebiten.Image

Returns the image corresponding to the underlying mpeg's video frame at the current Player.Position(). This means that as long as the mpeg is playing, calling this method at different times will return different frames.

The returned image is reused, so calling this method again is likely to overwrite its contents. This means you can use the image between calls, but you should not store it for later use expecting the image to remain the same.

func (*Player) Duration

func (self *Player) Duration() time.Duration

Returns the video duration.

func (*Player) FrameRate

func (self *Player) FrameRate() float64

Returns the video's framerate (how many video frames are used per second).

func (*Player) HasAudio

func (self *Player) HasAudio() bool

Returns whether the video has audio.

func (*Player) IsPlaying

func (self *Player) IsPlaying() bool

Returns whether the player's clock and audio are running or not. Notice that even when playing, video frames need to be retrieved manually through Player.CurrentFrame().

func (*Player) MPEG

func (self *Player) MPEG() (*mpeg.MPEG, *sync.Mutex)

Returns the player's underlying mpeg and its associated mutex. The mutex is particularly critical when the mpeg also contains audio, as the Ebitengine audio process may be periodically reading from the mpeg, making concurrent access dangerous.

Here's a list of common useful methods on the underlying mpeg:

  • SetAudioStream(int) to select the audio stream [0 - 4].
  • SetLoop(bool) to set mpeg looping mode.
  • Loop() bool to determine if the mpeg is in looping mode.

func (*Player) Pause

func (self *Player) Pause()

Pauses the player's playback clock. If the player is already paused, it just stays paused and nothing new happens.

If the underlying mpeg contains any audio, the audio will also be paused.

func (*Player) Play

func (self *Player) Play()

Play() activates the player's playback clock. If the player is already playing, it just keeps playing and nothing new happens.

If the underlying mpeg contains any audio, the audio will also start or resume. Video frames need to be retrieved manually through Player.CurrentFrame() instead.

func (*Player) Position

func (self *Player) Position() time.Duration

Returns the player's current playback position.

func (*Player) Resolution

func (self *Player) Resolution() (int, int)

Returns the width and height of the video.

func (*Player) Rewind

func (self *Player) Rewind()

Rewinds the mpeg streams to the beginning. Behavior may differ from [Player.Seek](0) (TODO: figure out the differences in more detail).

func (*Player) SeekFast

func (self *Player) SeekFast(position time.Duration)

Moves the player's playback position to the first intra-frame that can be found before the given target position.

Player.SeekPrecise() is more precise but can also be significantly slower.

TODO: better document the potential precision loss, common values in practical scenarios, or advice on format settings in order to make results more or less precise (if intra-frames can be easily configured). Also quantify how much slower "slower" means.

func (*Player) SeekPrecise

func (self *Player) SeekPrecise(position time.Duration)

Moves the player's playback position to the given one, relative to the start of the video.

This method is more precise than Player.SeekFast(), but it can also be significantly slower: a precise seek has to do an initial fast seek to the first intra-frame that can be found before the target position, but then it also has to keep decoding frames progressively until the actual target position is reached.

func (*Player) SetVolume

func (self *Player) SetVolume(volume float64)

Sets the volume of the video. If the video has no audio, this method will panic.

func (*Player) Volume

func (self *Player) Volume() float64

Gets the video's volume. If the video has no audio, 0 will be returned.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL