Comments added.

This commit is contained in:
zergon321
2021-08-27 00:53:42 +00:00
parent bac7062a86
commit d6f08ac53e
11 changed files with 128 additions and 2 deletions

View File

@@ -12,27 +12,40 @@ import (
)
const (
// StandardChannelCount is used for
// audio conversion while decoding
// audio frames.
StandardChannelCount = 2
)
// AudioStream is a stream containing
// audio frames consisting of audio samples.
type AudioStream struct {
baseStream
swrCtx *C.SwrContext
buffer *C.uint8_t
}
// ChannelCount returns the number of channels
// (1 for mono, 2 for stereo, etc.).
func (audio *AudioStream) ChannelCount() int {
return int(audio.codecParams.channels)
}
// SampleRate returns the sample rate of the
// audio stream.
func (audio *AudioStream) SampleRate() int {
return int(audio.codecParams.sample_rate)
}
// FrameSize returns the number of samples
// contained in one frame of the audio.
func (audio *AudioStream) FrameSize() int {
return int(audio.codecParams.frame_size)
}
// Open opens the audio stream to decode
// audio frames and samples from it.
func (audio *AudioStream) Open() error {
err := audio.open()
@@ -64,10 +77,12 @@ func (audio *AudioStream) Open() error {
return nil
}
// ReadFrame reads a new frame from the stream.
func (audio *AudioStream) ReadFrame() (Frame, bool, error) {
return audio.ReadAudioFrame()
}
// ReadAudioFrame reads a new audio frame from the stream.
func (audio *AudioStream) ReadAudioFrame() (*AudioFrame, bool, error) {
ok, err := audio.read()
@@ -122,6 +137,8 @@ func (audio *AudioStream) ReadAudioFrame() (*AudioFrame, bool, error) {
return frame, true, nil
}
// Close closes the audio stream and
// stops decoding audio frames.
func (audio *AudioStream) Close() error {
err := audio.close()

View File

@@ -1,14 +1,19 @@
package reisen
// AudioFrame is a data frame
// obtained from an audio stream.
type AudioFrame struct {
baseFrame
data []byte
}
// Data returns a raw slice of
// audio frame samples.
func (frame *AudioFrame) Data() []byte {
return frame.data
}
// newAudioFrame returns a newly created audio frame.
func newAudioFrame(stream Stream, pts int64, data []byte) *AudioFrame {
frame := new(AudioFrame)

View File

@@ -3,7 +3,15 @@ package reisen
type ErrorType int
const (
// ErrorAgain is returned when
// the decoder needs more data
// to serve the frame.
ErrorAgain ErrorType = -11
// ErrorInvalidValue is returned
// when the function call argument
// is invalid.
ErrorInvalidValue ErrorType = -22
// ErrorEndOfFile is returned upon
// reaching the end of the media file.
ErrorEndOfFile ErrorType = -541478725
)

View File

@@ -103,6 +103,11 @@ func main() {
}
}
for _, stream := range media.Streams() {
err = stream.Close()
handleError(err)
}
err = media.CloseDecode()
handleError(err)
}

View File

@@ -5,16 +5,22 @@ import (
"time"
)
// Frame is an abstract data frame.
type Frame interface {
Data() []byte
PresentationOffset() (time.Duration, error)
}
// baseFrame contains the information
// common for all frames of any type.
type baseFrame struct {
stream Stream
pts int64
}
// PresentationOffset returns the duration offset
// since the start of the media at which the frame
// should be played.
func (frame *baseFrame) PresentationOffset() (time.Duration, error) {
tbNum, tbDen := frame.stream.TimeBase()
tb := float64(tbNum) / float64(tbDen)

View File

@@ -12,16 +12,21 @@ import (
"unsafe"
)
// Media is a media file containing
// audio, video and other types of streams.
type Media struct {
ctx *C.AVFormatContext
packet *C.AVPacket
streams []Stream
}
// StreamCount returns the number of streams.
func (media *Media) StreamCount() int {
return int(media.ctx.nb_streams)
}
// Streams returns a slice of all the available
// media data streams.
func (media *Media) Streams() []Stream {
streams := make([]Stream, len(media.streams))
copy(streams, media.streams)
@@ -29,6 +34,8 @@ func (media *Media) Streams() []Stream {
return streams
}
// Duration returns the overall duration
// of the media file.
func (media *Media) Duration() (time.Duration, error) {
dur := media.ctx.duration
tm := float64(dur) / float64(TimeBase)
@@ -36,6 +43,7 @@ func (media *Media) Duration() (time.Duration, error) {
return time.ParseDuration(fmt.Sprintf("%fs", tm))
}
// FormatName returns the name of the media format.
func (media *Media) FormatName() string {
if media.ctx.iformat.name == nil {
return ""
@@ -44,6 +52,8 @@ func (media *Media) FormatName() string {
return C.GoString(media.ctx.iformat.name)
}
// FormatLongName returns the long name
// of the media container.
func (media *Media) FormatLongName() string {
if media.ctx.iformat.long_name == nil {
return ""
@@ -52,6 +62,8 @@ func (media *Media) FormatLongName() string {
return C.GoString(media.ctx.iformat.long_name)
}
// FormatMIMEType returns the MIME type name
// of the media container.
func (media *Media) FormatMIMEType() string {
if media.ctx.iformat.mime_type == nil {
return ""
@@ -60,6 +72,8 @@ func (media *Media) FormatMIMEType() string {
return C.GoString(media.ctx.iformat.mime_type)
}
// findStreams retrieves the stream information
// from the media container.
func (media *Media) findStreams() error {
streams := []Stream{}
innerStreams := unsafe.Slice(
@@ -110,6 +124,9 @@ func (media *Media) findStreams() error {
return nil
}
// OpenDecode opens the media container for decoding.
//
// CloseDecode() should be called afterwards.
func (media *Media) OpenDecode() error {
media.packet = C.av_packet_alloc()
@@ -121,6 +138,7 @@ func (media *Media) OpenDecode() error {
return nil
}
// ReadPacket reads the next packet from the media stream.
func (media *Media) ReadPacket() (*Packet, bool, error) {
status := C.av_read_frame(media.ctx, media.packet)
@@ -136,15 +154,19 @@ func (media *Media) ReadPacket() (*Packet, bool, error) {
return &Packet{media: media}, true, nil
}
// CloseDecode closes the media container for decoding.
func (media *Media) CloseDecode() error {
C.av_free(unsafe.Pointer(media.packet))
return nil
}
// Close closes the media container.
func (media *Media) Close() {
C.avformat_free_context(media.ctx)
}
// NewMedia returns a new media container analyzer
// for the specified media file.
func NewMedia(filename string) (*Media, error) {
media := &Media{
ctx: C.avformat_alloc_context(),

View File

@@ -7,14 +7,23 @@ package reisen
// #include <libswscale/swscale.h>
import "C"
// Packet is a piece of data
// acquired from the media container.
//
// It can be either a video frame or
// an audio frame.
type Packet struct {
media *Media
}
// StreamIndex returns the index of the
// stream the packet belongs to.
func (pkt *Packet) StreamIndex() int {
return int(pkt.media.packet.stream_index)
}
// Type returns the type of the packet
// (video or audio).
func (pkt *Packet) Type() StreamType {
return pkt.media.Streams()[pkt.StreamIndex()].Type()
}

View File

@@ -11,13 +11,19 @@ import (
"unsafe"
)
// StreamType is a type of
// a media stream.
type StreamType int
const (
// StreamVideo denotes the stream keeping video frames.
StreamVideo StreamType = C.AVMEDIA_TYPE_VIDEO
// StreamAudio denotes the stream keeping audio frames.
StreamAudio StreamType = C.AVMEDIA_TYPE_AUDIO
)
// String returns the string representation of
// stream type identifier.
func (streamType StreamType) String() string {
switch streamType {
case StreamVideo:
@@ -34,6 +40,7 @@ func (streamType StreamType) String() string {
// TODO: add an opportunity to
// receive duration in time base units.
// Stream is an abstract media data stream.
type Stream interface {
Index() int
Type() StreamType
@@ -49,6 +56,8 @@ type Stream interface {
Close() error
}
// baseStream holds the information
// common for all media data streams.
type baseStream struct {
media *Media
inner *C.AVStream
@@ -60,18 +69,24 @@ type baseStream struct {
opened bool
}
// Opened returns 'true' if the stream
// is opened for decoding, and 'false' otherwise.
func (stream *baseStream) Opened() bool {
return stream.opened
}
// Index returns the index of the stream.
func (stream *baseStream) Index() int {
return int(stream.inner.index)
}
// Type returns the stream media data type.
func (stream *baseStream) Type() StreamType {
return StreamType(stream.codecParams.codec_type)
}
// CodecName returns the name of the codec
// that was used for encoding the stream.
func (stream *baseStream) CodecName() string {
if stream.codec.name == nil {
return ""
@@ -80,6 +95,8 @@ func (stream *baseStream) CodecName() string {
return C.GoString(stream.codec.name)
}
// CodecName returns the long name of the
// codec that was used for encoding the stream.
func (stream *baseStream) CodecLongName() string {
if stream.codec.long_name == nil {
return ""
@@ -88,10 +105,12 @@ func (stream *baseStream) CodecLongName() string {
return C.GoString(stream.codec.long_name)
}
// BitRate returns the bit rate of the stream (in bps).
func (stream *baseStream) BitRate() int64 {
return int64(stream.codecParams.bit_rate)
}
// Duration returns the duration of the stream.
func (stream *baseStream) Duration() (time.Duration, error) {
dur := stream.inner.duration
@@ -106,20 +125,31 @@ func (stream *baseStream) Duration() (time.Duration, error) {
return time.ParseDuration(fmt.Sprintf("%fs", tm))
}
// TimeBase the numerator and the denominator of the
// stream time base factor fraction.
//
// All the duration values of the stream are
// multiplied by this factor to get duration
// in seconds.
func (stream *baseStream) TimeBase() (int, int) {
return int(stream.inner.time_base.num),
int(stream.inner.time_base.den)
}
// FrameRate returns the frame rate of the stream
// as a fraction with a numerator and a denominator.
func (stream *baseStream) FrameRate() (int, int) {
return int(stream.inner.r_frame_rate.num),
int(stream.inner.r_frame_rate.den)
}
// FrameCount returns the total number of frames
// in the stream.
func (stream *baseStream) FrameCount() int64 {
return int64(stream.inner.nb_frames)
}
// open opens the stream for decoding.
func (stream *baseStream) open() error {
stream.codecCtx = C.avcodec_alloc_context3(stream.codec)
@@ -154,6 +184,8 @@ func (stream *baseStream) open() error {
return nil
}
// read decodes the packet and obtains a
// frame from it.
func (stream *baseStream) read() (bool, error) {
status := C.avcodec_send_packet(
stream.codecCtx, stream.media.packet)
@@ -187,6 +219,7 @@ func (stream *baseStream) read() (bool, error) {
return true, nil
}
// close closes the stream for decoding.
func (stream *baseStream) close() error {
C.av_free(unsafe.Pointer(stream.frame))

View File

@@ -5,5 +5,7 @@ package reisen
import "C"
const (
// TimeBase is a global time base
// used for describing media containers.
TimeBase int = C.AV_TIME_BASE
)

View File

@@ -13,6 +13,8 @@ import (
"unsafe"
)
// VideoStream is a streaming holding
// video frames.
type VideoStream struct {
baseStream
swsCtx *C.struct_SwsContext
@@ -20,19 +22,26 @@ type VideoStream struct {
bufSize C.int
}
// AspectRatio returns the fraction of the video
// stream frame aspect ratio (1/0 if unknown).
func (video *VideoStream) AspectRatio() (int, int) {
return int(video.codecParams.sample_aspect_ratio.num),
int(video.codecParams.sample_aspect_ratio.den)
}
// Width returns the width of the video
// stream frame.
func (video *VideoStream) Width() int {
return int(video.codecParams.width)
}
// Height returns the height of the video
// stream frame.
func (video *VideoStream) Height() int {
return int(video.codecParams.height)
}
// Open opens the video stream for decoding.
func (video *VideoStream) Open() error {
err := video.open()
@@ -86,10 +95,13 @@ func (video *VideoStream) Open() error {
return nil
}
// ReadFrame reads the next frame from the stream.
func (video *VideoStream) ReadFrame() (Frame, bool, error) {
return video.ReadVideoFrame()
}
// ReadVideoFrame reads the next video frame
// from the video stream.
func (video *VideoStream) ReadVideoFrame() (*VideoFrame, bool, error) {
ok, err := video.read()
@@ -121,6 +133,7 @@ func (video *VideoStream) ReadVideoFrame() (*VideoFrame, bool, error) {
return frame, true, nil
}
// Close closes the video stream for decoding.
func (video *VideoStream) Close() error {
err := video.close()

View File

@@ -2,19 +2,25 @@ package reisen
import "image"
// VideoFrame is a single frame
// of a video stream.
type VideoFrame struct {
baseFrame
img *image.RGBA
}
// Data returns a byte slice of RGBA
// pixels of the frame image.
func (frame *VideoFrame) Data() []byte {
return frame.img.Pix
}
// Image returns the RGBA image of the frame.
func (frame *VideoFrame) Image() *image.RGBA {
return frame.img
}
// newVideoFrame returns a newly created video frame.
func newVideoFrame(stream Stream, pts int64, width, height int, pix []byte) *VideoFrame {
upLeft := image.Point{0, 0}
lowRight := image.Point{width, height}