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 (
|
||||
// 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()
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
12
errors.go
12
errors.go
@@ -3,7 +3,15 @@ package reisen
|
||||
type ErrorType int
|
||||
|
||||
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
|
||||
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()
|
||||
handleError(err)
|
||||
}
|
||||
|
6
frame.go
6
frame.go
@@ -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)
|
||||
|
22
media.go
22
media.go
@@ -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(),
|
||||
|
@@ -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()
|
||||
}
|
||||
|
33
stream.go
33
stream.go
@@ -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))
|
||||
|
||||
|
2
time.go
2
time.go
@@ -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
|
||||
)
|
||||
|
13
video.go
13
video.go
@@ -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()
|
||||
|
||||
|
@@ -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}
|
||||
|
Reference in New Issue
Block a user