mirror of
https://github.com/zergon321/reisen.git
synced 2025-09-26 20:01:14 +08:00
Comments added.
This commit is contained in:
17
audio.go
17
audio.go
@@ -12,27 +12,40 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// StandardChannelCount is used for
|
||||||
|
// audio conversion while decoding
|
||||||
|
// audio frames.
|
||||||
StandardChannelCount = 2
|
StandardChannelCount = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AudioStream is a stream containing
|
||||||
|
// audio frames consisting of audio samples.
|
||||||
type AudioStream struct {
|
type AudioStream struct {
|
||||||
baseStream
|
baseStream
|
||||||
swrCtx *C.SwrContext
|
swrCtx *C.SwrContext
|
||||||
buffer *C.uint8_t
|
buffer *C.uint8_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelCount returns the number of channels
|
||||||
|
// (1 for mono, 2 for stereo, etc.).
|
||||||
func (audio *AudioStream) ChannelCount() int {
|
func (audio *AudioStream) ChannelCount() int {
|
||||||
return int(audio.codecParams.channels)
|
return int(audio.codecParams.channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SampleRate returns the sample rate of the
|
||||||
|
// audio stream.
|
||||||
func (audio *AudioStream) SampleRate() int {
|
func (audio *AudioStream) SampleRate() int {
|
||||||
return int(audio.codecParams.sample_rate)
|
return int(audio.codecParams.sample_rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FrameSize returns the number of samples
|
||||||
|
// contained in one frame of the audio.
|
||||||
func (audio *AudioStream) FrameSize() int {
|
func (audio *AudioStream) FrameSize() int {
|
||||||
return int(audio.codecParams.frame_size)
|
return int(audio.codecParams.frame_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open opens the audio stream to decode
|
||||||
|
// audio frames and samples from it.
|
||||||
func (audio *AudioStream) Open() error {
|
func (audio *AudioStream) Open() error {
|
||||||
err := audio.open()
|
err := audio.open()
|
||||||
|
|
||||||
@@ -64,10 +77,12 @@ func (audio *AudioStream) Open() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFrame reads a new frame from the stream.
|
||||||
func (audio *AudioStream) ReadFrame() (Frame, bool, error) {
|
func (audio *AudioStream) ReadFrame() (Frame, bool, error) {
|
||||||
return audio.ReadAudioFrame()
|
return audio.ReadAudioFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadAudioFrame reads a new audio frame from the stream.
|
||||||
func (audio *AudioStream) ReadAudioFrame() (*AudioFrame, bool, error) {
|
func (audio *AudioStream) ReadAudioFrame() (*AudioFrame, bool, error) {
|
||||||
ok, err := audio.read()
|
ok, err := audio.read()
|
||||||
|
|
||||||
@@ -122,6 +137,8 @@ func (audio *AudioStream) ReadAudioFrame() (*AudioFrame, bool, error) {
|
|||||||
return frame, true, nil
|
return frame, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the audio stream and
|
||||||
|
// stops decoding audio frames.
|
||||||
func (audio *AudioStream) Close() error {
|
func (audio *AudioStream) Close() error {
|
||||||
err := audio.close()
|
err := audio.close()
|
||||||
|
|
||||||
|
@@ -1,14 +1,19 @@
|
|||||||
package reisen
|
package reisen
|
||||||
|
|
||||||
|
// AudioFrame is a data frame
|
||||||
|
// obtained from an audio stream.
|
||||||
type AudioFrame struct {
|
type AudioFrame struct {
|
||||||
baseFrame
|
baseFrame
|
||||||
data []byte
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data returns a raw slice of
|
||||||
|
// audio frame samples.
|
||||||
func (frame *AudioFrame) Data() []byte {
|
func (frame *AudioFrame) Data() []byte {
|
||||||
return frame.data
|
return frame.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newAudioFrame returns a newly created audio frame.
|
||||||
func newAudioFrame(stream Stream, pts int64, data []byte) *AudioFrame {
|
func newAudioFrame(stream Stream, pts int64, data []byte) *AudioFrame {
|
||||||
frame := new(AudioFrame)
|
frame := new(AudioFrame)
|
||||||
|
|
||||||
|
12
errors.go
12
errors.go
@@ -3,7 +3,15 @@ package reisen
|
|||||||
type ErrorType int
|
type ErrorType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ErrorAgain ErrorType = -11
|
// 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
|
ErrorInvalidValue ErrorType = -22
|
||||||
ErrorEndOfFile ErrorType = -541478725
|
// ErrorEndOfFile is returned upon
|
||||||
|
// reaching the end of the media file.
|
||||||
|
ErrorEndOfFile ErrorType = -541478725
|
||||||
)
|
)
|
||||||
|
@@ -103,6 +103,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, stream := range media.Streams() {
|
||||||
|
err = stream.Close()
|
||||||
|
handleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = media.CloseDecode()
|
err = media.CloseDecode()
|
||||||
handleError(err)
|
handleError(err)
|
||||||
}
|
}
|
||||||
|
6
frame.go
6
frame.go
@@ -5,16 +5,22 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Frame is an abstract data frame.
|
||||||
type Frame interface {
|
type Frame interface {
|
||||||
Data() []byte
|
Data() []byte
|
||||||
PresentationOffset() (time.Duration, error)
|
PresentationOffset() (time.Duration, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// baseFrame contains the information
|
||||||
|
// common for all frames of any type.
|
||||||
type baseFrame struct {
|
type baseFrame struct {
|
||||||
stream Stream
|
stream Stream
|
||||||
pts int64
|
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) {
|
func (frame *baseFrame) PresentationOffset() (time.Duration, error) {
|
||||||
tbNum, tbDen := frame.stream.TimeBase()
|
tbNum, tbDen := frame.stream.TimeBase()
|
||||||
tb := float64(tbNum) / float64(tbDen)
|
tb := float64(tbNum) / float64(tbDen)
|
||||||
|
22
media.go
22
media.go
@@ -12,16 +12,21 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Media is a media file containing
|
||||||
|
// audio, video and other types of streams.
|
||||||
type Media struct {
|
type Media struct {
|
||||||
ctx *C.AVFormatContext
|
ctx *C.AVFormatContext
|
||||||
packet *C.AVPacket
|
packet *C.AVPacket
|
||||||
streams []Stream
|
streams []Stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StreamCount returns the number of streams.
|
||||||
func (media *Media) StreamCount() int {
|
func (media *Media) StreamCount() int {
|
||||||
return int(media.ctx.nb_streams)
|
return int(media.ctx.nb_streams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Streams returns a slice of all the available
|
||||||
|
// media data streams.
|
||||||
func (media *Media) Streams() []Stream {
|
func (media *Media) Streams() []Stream {
|
||||||
streams := make([]Stream, len(media.streams))
|
streams := make([]Stream, len(media.streams))
|
||||||
copy(streams, media.streams)
|
copy(streams, media.streams)
|
||||||
@@ -29,6 +34,8 @@ func (media *Media) Streams() []Stream {
|
|||||||
return streams
|
return streams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duration returns the overall duration
|
||||||
|
// of the media file.
|
||||||
func (media *Media) Duration() (time.Duration, error) {
|
func (media *Media) Duration() (time.Duration, error) {
|
||||||
dur := media.ctx.duration
|
dur := media.ctx.duration
|
||||||
tm := float64(dur) / float64(TimeBase)
|
tm := float64(dur) / float64(TimeBase)
|
||||||
@@ -36,6 +43,7 @@ func (media *Media) Duration() (time.Duration, error) {
|
|||||||
return time.ParseDuration(fmt.Sprintf("%fs", tm))
|
return time.ParseDuration(fmt.Sprintf("%fs", tm))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatName returns the name of the media format.
|
||||||
func (media *Media) FormatName() string {
|
func (media *Media) FormatName() string {
|
||||||
if media.ctx.iformat.name == nil {
|
if media.ctx.iformat.name == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -44,6 +52,8 @@ func (media *Media) FormatName() string {
|
|||||||
return C.GoString(media.ctx.iformat.name)
|
return C.GoString(media.ctx.iformat.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatLongName returns the long name
|
||||||
|
// of the media container.
|
||||||
func (media *Media) FormatLongName() string {
|
func (media *Media) FormatLongName() string {
|
||||||
if media.ctx.iformat.long_name == nil {
|
if media.ctx.iformat.long_name == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -52,6 +62,8 @@ func (media *Media) FormatLongName() string {
|
|||||||
return C.GoString(media.ctx.iformat.long_name)
|
return C.GoString(media.ctx.iformat.long_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatMIMEType returns the MIME type name
|
||||||
|
// of the media container.
|
||||||
func (media *Media) FormatMIMEType() string {
|
func (media *Media) FormatMIMEType() string {
|
||||||
if media.ctx.iformat.mime_type == nil {
|
if media.ctx.iformat.mime_type == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -60,6 +72,8 @@ func (media *Media) FormatMIMEType() string {
|
|||||||
return C.GoString(media.ctx.iformat.mime_type)
|
return C.GoString(media.ctx.iformat.mime_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findStreams retrieves the stream information
|
||||||
|
// from the media container.
|
||||||
func (media *Media) findStreams() error {
|
func (media *Media) findStreams() error {
|
||||||
streams := []Stream{}
|
streams := []Stream{}
|
||||||
innerStreams := unsafe.Slice(
|
innerStreams := unsafe.Slice(
|
||||||
@@ -110,6 +124,9 @@ func (media *Media) findStreams() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenDecode opens the media container for decoding.
|
||||||
|
//
|
||||||
|
// CloseDecode() should be called afterwards.
|
||||||
func (media *Media) OpenDecode() error {
|
func (media *Media) OpenDecode() error {
|
||||||
media.packet = C.av_packet_alloc()
|
media.packet = C.av_packet_alloc()
|
||||||
|
|
||||||
@@ -121,6 +138,7 @@ func (media *Media) OpenDecode() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadPacket reads the next packet from the media stream.
|
||||||
func (media *Media) ReadPacket() (*Packet, bool, error) {
|
func (media *Media) ReadPacket() (*Packet, bool, error) {
|
||||||
status := C.av_read_frame(media.ctx, media.packet)
|
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
|
return &Packet{media: media}, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseDecode closes the media container for decoding.
|
||||||
func (media *Media) CloseDecode() error {
|
func (media *Media) CloseDecode() error {
|
||||||
C.av_free(unsafe.Pointer(media.packet))
|
C.av_free(unsafe.Pointer(media.packet))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the media container.
|
||||||
func (media *Media) Close() {
|
func (media *Media) Close() {
|
||||||
C.avformat_free_context(media.ctx)
|
C.avformat_free_context(media.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMedia returns a new media container analyzer
|
||||||
|
// for the specified media file.
|
||||||
func NewMedia(filename string) (*Media, error) {
|
func NewMedia(filename string) (*Media, error) {
|
||||||
media := &Media{
|
media := &Media{
|
||||||
ctx: C.avformat_alloc_context(),
|
ctx: C.avformat_alloc_context(),
|
||||||
|
@@ -7,14 +7,23 @@ package reisen
|
|||||||
// #include <libswscale/swscale.h>
|
// #include <libswscale/swscale.h>
|
||||||
import "C"
|
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 {
|
type Packet struct {
|
||||||
media *Media
|
media *Media
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StreamIndex returns the index of the
|
||||||
|
// stream the packet belongs to.
|
||||||
func (pkt *Packet) StreamIndex() int {
|
func (pkt *Packet) StreamIndex() int {
|
||||||
return int(pkt.media.packet.stream_index)
|
return int(pkt.media.packet.stream_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the type of the packet
|
||||||
|
// (video or audio).
|
||||||
func (pkt *Packet) Type() StreamType {
|
func (pkt *Packet) Type() StreamType {
|
||||||
return pkt.media.Streams()[pkt.StreamIndex()].Type()
|
return pkt.media.Streams()[pkt.StreamIndex()].Type()
|
||||||
}
|
}
|
||||||
|
33
stream.go
33
stream.go
@@ -11,13 +11,19 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StreamType is a type of
|
||||||
|
// a media stream.
|
||||||
type StreamType int
|
type StreamType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// StreamVideo denotes the stream keeping video frames.
|
||||||
StreamVideo StreamType = C.AVMEDIA_TYPE_VIDEO
|
StreamVideo StreamType = C.AVMEDIA_TYPE_VIDEO
|
||||||
|
// StreamAudio denotes the stream keeping audio frames.
|
||||||
StreamAudio StreamType = C.AVMEDIA_TYPE_AUDIO
|
StreamAudio StreamType = C.AVMEDIA_TYPE_AUDIO
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of
|
||||||
|
// stream type identifier.
|
||||||
func (streamType StreamType) String() string {
|
func (streamType StreamType) String() string {
|
||||||
switch streamType {
|
switch streamType {
|
||||||
case StreamVideo:
|
case StreamVideo:
|
||||||
@@ -34,6 +40,7 @@ func (streamType StreamType) String() string {
|
|||||||
// TODO: add an opportunity to
|
// TODO: add an opportunity to
|
||||||
// receive duration in time base units.
|
// receive duration in time base units.
|
||||||
|
|
||||||
|
// Stream is an abstract media data stream.
|
||||||
type Stream interface {
|
type Stream interface {
|
||||||
Index() int
|
Index() int
|
||||||
Type() StreamType
|
Type() StreamType
|
||||||
@@ -49,6 +56,8 @@ type Stream interface {
|
|||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// baseStream holds the information
|
||||||
|
// common for all media data streams.
|
||||||
type baseStream struct {
|
type baseStream struct {
|
||||||
media *Media
|
media *Media
|
||||||
inner *C.AVStream
|
inner *C.AVStream
|
||||||
@@ -60,18 +69,24 @@ type baseStream struct {
|
|||||||
opened bool
|
opened bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opened returns 'true' if the stream
|
||||||
|
// is opened for decoding, and 'false' otherwise.
|
||||||
func (stream *baseStream) Opened() bool {
|
func (stream *baseStream) Opened() bool {
|
||||||
return stream.opened
|
return stream.opened
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index returns the index of the stream.
|
||||||
func (stream *baseStream) Index() int {
|
func (stream *baseStream) Index() int {
|
||||||
return int(stream.inner.index)
|
return int(stream.inner.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the stream media data type.
|
||||||
func (stream *baseStream) Type() StreamType {
|
func (stream *baseStream) Type() StreamType {
|
||||||
return StreamType(stream.codecParams.codec_type)
|
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 {
|
func (stream *baseStream) CodecName() string {
|
||||||
if stream.codec.name == nil {
|
if stream.codec.name == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -80,6 +95,8 @@ func (stream *baseStream) CodecName() string {
|
|||||||
return C.GoString(stream.codec.name)
|
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 {
|
func (stream *baseStream) CodecLongName() string {
|
||||||
if stream.codec.long_name == nil {
|
if stream.codec.long_name == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -88,10 +105,12 @@ func (stream *baseStream) CodecLongName() string {
|
|||||||
return C.GoString(stream.codec.long_name)
|
return C.GoString(stream.codec.long_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BitRate returns the bit rate of the stream (in bps).
|
||||||
func (stream *baseStream) BitRate() int64 {
|
func (stream *baseStream) BitRate() int64 {
|
||||||
return int64(stream.codecParams.bit_rate)
|
return int64(stream.codecParams.bit_rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duration returns the duration of the stream.
|
||||||
func (stream *baseStream) Duration() (time.Duration, error) {
|
func (stream *baseStream) Duration() (time.Duration, error) {
|
||||||
dur := stream.inner.duration
|
dur := stream.inner.duration
|
||||||
|
|
||||||
@@ -106,20 +125,31 @@ func (stream *baseStream) Duration() (time.Duration, error) {
|
|||||||
return time.ParseDuration(fmt.Sprintf("%fs", tm))
|
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) {
|
func (stream *baseStream) TimeBase() (int, int) {
|
||||||
return int(stream.inner.time_base.num),
|
return int(stream.inner.time_base.num),
|
||||||
int(stream.inner.time_base.den)
|
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) {
|
func (stream *baseStream) FrameRate() (int, int) {
|
||||||
return int(stream.inner.r_frame_rate.num),
|
return int(stream.inner.r_frame_rate.num),
|
||||||
int(stream.inner.r_frame_rate.den)
|
int(stream.inner.r_frame_rate.den)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FrameCount returns the total number of frames
|
||||||
|
// in the stream.
|
||||||
func (stream *baseStream) FrameCount() int64 {
|
func (stream *baseStream) FrameCount() int64 {
|
||||||
return int64(stream.inner.nb_frames)
|
return int64(stream.inner.nb_frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// open opens the stream for decoding.
|
||||||
func (stream *baseStream) open() error {
|
func (stream *baseStream) open() error {
|
||||||
stream.codecCtx = C.avcodec_alloc_context3(stream.codec)
|
stream.codecCtx = C.avcodec_alloc_context3(stream.codec)
|
||||||
|
|
||||||
@@ -154,6 +184,8 @@ func (stream *baseStream) open() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read decodes the packet and obtains a
|
||||||
|
// frame from it.
|
||||||
func (stream *baseStream) read() (bool, error) {
|
func (stream *baseStream) read() (bool, error) {
|
||||||
status := C.avcodec_send_packet(
|
status := C.avcodec_send_packet(
|
||||||
stream.codecCtx, stream.media.packet)
|
stream.codecCtx, stream.media.packet)
|
||||||
@@ -187,6 +219,7 @@ func (stream *baseStream) read() (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close closes the stream for decoding.
|
||||||
func (stream *baseStream) close() error {
|
func (stream *baseStream) close() error {
|
||||||
C.av_free(unsafe.Pointer(stream.frame))
|
C.av_free(unsafe.Pointer(stream.frame))
|
||||||
|
|
||||||
|
2
time.go
2
time.go
@@ -5,5 +5,7 @@ package reisen
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TimeBase is a global time base
|
||||||
|
// used for describing media containers.
|
||||||
TimeBase int = C.AV_TIME_BASE
|
TimeBase int = C.AV_TIME_BASE
|
||||||
)
|
)
|
||||||
|
13
video.go
13
video.go
@@ -13,6 +13,8 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VideoStream is a streaming holding
|
||||||
|
// video frames.
|
||||||
type VideoStream struct {
|
type VideoStream struct {
|
||||||
baseStream
|
baseStream
|
||||||
swsCtx *C.struct_SwsContext
|
swsCtx *C.struct_SwsContext
|
||||||
@@ -20,19 +22,26 @@ type VideoStream struct {
|
|||||||
bufSize C.int
|
bufSize C.int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AspectRatio returns the fraction of the video
|
||||||
|
// stream frame aspect ratio (1/0 if unknown).
|
||||||
func (video *VideoStream) AspectRatio() (int, int) {
|
func (video *VideoStream) AspectRatio() (int, int) {
|
||||||
return int(video.codecParams.sample_aspect_ratio.num),
|
return int(video.codecParams.sample_aspect_ratio.num),
|
||||||
int(video.codecParams.sample_aspect_ratio.den)
|
int(video.codecParams.sample_aspect_ratio.den)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Width returns the width of the video
|
||||||
|
// stream frame.
|
||||||
func (video *VideoStream) Width() int {
|
func (video *VideoStream) Width() int {
|
||||||
return int(video.codecParams.width)
|
return int(video.codecParams.width)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Height returns the height of the video
|
||||||
|
// stream frame.
|
||||||
func (video *VideoStream) Height() int {
|
func (video *VideoStream) Height() int {
|
||||||
return int(video.codecParams.height)
|
return int(video.codecParams.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open opens the video stream for decoding.
|
||||||
func (video *VideoStream) Open() error {
|
func (video *VideoStream) Open() error {
|
||||||
err := video.open()
|
err := video.open()
|
||||||
|
|
||||||
@@ -86,10 +95,13 @@ func (video *VideoStream) Open() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFrame reads the next frame from the stream.
|
||||||
func (video *VideoStream) ReadFrame() (Frame, bool, error) {
|
func (video *VideoStream) ReadFrame() (Frame, bool, error) {
|
||||||
return video.ReadVideoFrame()
|
return video.ReadVideoFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadVideoFrame reads the next video frame
|
||||||
|
// from the video stream.
|
||||||
func (video *VideoStream) ReadVideoFrame() (*VideoFrame, bool, error) {
|
func (video *VideoStream) ReadVideoFrame() (*VideoFrame, bool, error) {
|
||||||
ok, err := video.read()
|
ok, err := video.read()
|
||||||
|
|
||||||
@@ -121,6 +133,7 @@ func (video *VideoStream) ReadVideoFrame() (*VideoFrame, bool, error) {
|
|||||||
return frame, true, nil
|
return frame, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the video stream for decoding.
|
||||||
func (video *VideoStream) Close() error {
|
func (video *VideoStream) Close() error {
|
||||||
err := video.close()
|
err := video.close()
|
||||||
|
|
||||||
|
@@ -2,19 +2,25 @@ package reisen
|
|||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
|
|
||||||
|
// VideoFrame is a single frame
|
||||||
|
// of a video stream.
|
||||||
type VideoFrame struct {
|
type VideoFrame struct {
|
||||||
baseFrame
|
baseFrame
|
||||||
img *image.RGBA
|
img *image.RGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data returns a byte slice of RGBA
|
||||||
|
// pixels of the frame image.
|
||||||
func (frame *VideoFrame) Data() []byte {
|
func (frame *VideoFrame) Data() []byte {
|
||||||
return frame.img.Pix
|
return frame.img.Pix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image returns the RGBA image of the frame.
|
||||||
func (frame *VideoFrame) Image() *image.RGBA {
|
func (frame *VideoFrame) Image() *image.RGBA {
|
||||||
return frame.img
|
return frame.img
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newVideoFrame returns a newly created video frame.
|
||||||
func newVideoFrame(stream Stream, pts int64, width, height int, pix []byte) *VideoFrame {
|
func newVideoFrame(stream Stream, pts int64, width, height int, pix []byte) *VideoFrame {
|
||||||
upLeft := image.Point{0, 0}
|
upLeft := image.Point{0, 0}
|
||||||
lowRight := image.Point{width, height}
|
lowRight := image.Point{width, height}
|
||||||
|
Reference in New Issue
Block a user