func NewAudioFrame(sampleFormat int32, channels, nb_samples int) (*Frame, error) { this := NewFrame() this.mediaType = AVMEDIA_TYPE_AUDIO this.SetNbSamples(nb_samples) this.SetFormat(sampleFormat) this.SetChannelLayout(channels) //the codec gives us the frame size, in samples, //we calculate the size of the samples buffer in bytes size := C.av_samples_get_buffer_size(nil, C.int(channels), C.int(nb_samples), sampleFormat, 0) if size < 0 { return nil, errors.New("Could not get sample buffer size") } samples := (*_Ctype_uint8_t)(C.av_malloc(C.size_t(size))) if samples == nil { return nil, errors.New(fmt.Sprintf("Could not allocate %d bytes for samples buffer", size)) } //setup the data pointers in the AVFrame ret := int(C.avcodec_fill_audio_frame(this.avFrame, C.int(channels), sampleFormat, samples, C.int(size), 0)) if ret < 0 { return nil, errors.New("Could not setup audio frame") } return this, nil }
func (pd *ProbeData) SetBuffer(buffer []byte) { size := C.size_t(len(buffer)) extraSize := C.size_t(C.AVPROBE_PADDING_SIZE) buf := (*C.uchar)(C.av_malloc(size + extraSize)) if len(buffer) > 0 { C.memcpy(unsafe.Pointer(buf), unsafe.Pointer(&buffer[0]), size) } C.free(unsafe.Pointer(pd.CAVProbeData.buf)) pd.CAVProbeData.buf = buf pd.CAVProbeData.buf_size = C.int(size) }
// AVIOContext constructor. Use it only if You need custom IO behaviour! func NewAVIOContext(ctx *FmtCtx, handlers *AVIOHandlers) (*AVIOContext, error) { this := &AVIOContext{} buffer := (*C.uchar)(C.av_malloc(C.size_t(IO_BUFFER_SIZE))) if buffer == nil { return nil, errors.New("unable to allocate buffer") } // we have to explicitly set it to nil, to force library using default handlers var ptrRead, ptrWrite, ptrSeek *[0]byte = nil, nil, nil if handlers != nil { if handlersMap == nil { handlersMap = make(map[uintptr]*AVIOHandlers) } handlersMap[uintptr(unsafe.Pointer(ctx.avCtx))] = handlers this.handlerKey = uintptr(unsafe.Pointer(ctx.avCtx)) } if handlers.ReadPacket != nil { ptrRead = (*[0]byte)(C.readCallBack) } if handlers.WritePacket != nil { ptrWrite = (*[0]byte)(C.writeCallBack) } if handlers.Seek != nil { ptrSeek = (*[0]byte)(C.seekCallBack) } if this.avAVIOContext = C.avio_alloc_context(buffer, C.int(IO_BUFFER_SIZE), 0, unsafe.Pointer(ctx.avCtx), ptrRead, ptrWrite, ptrSeek); this.avAVIOContext == nil { return nil, errors.New("unable to initialize avio context") } return this, nil }
func (c *Decoder) decodeAudio(p Packet) *Frame { packet := new(C.AVPacket) C.av_init_packet(packet) defer C.av_free_packet(packet) packet.pts = C.int64_t(p.Pts) packet.dts = C.int64_t(p.Dts) packet.size = C.int(p.Size) packet.data = (*C.uint8_t)(unsafe.Pointer(&p.Data[0])) packet.stream_index = C.int(p.Stream) packet.flags = C.int(p.Flags) packet.duration = C.int(p.Duration) packet.pos = C.int64_t(p.Pos) //size:=packet.size; samples_size := C.int(C.AVCODEC_MAX_AUDIO_FRAME_SIZE) //bps := C.av_get_bits_per_sample_fmt(c.Ctx.sample_fmt) >> 3; outbuf := (*C.uint8_t)(C.av_malloc(C.uint(samples_size))) defer C.av_free(unsafe.Pointer(outbuf)) C.avcodec_decode_audio3(c.Ctx, (*C.int16_t)(unsafe.Pointer(outbuf)), &samples_size, packet) //println(data_len) return nil }
//Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if available on the CPU). func AvMalloc(s uintptr) unsafe.Pointer { return unsafe.Pointer(C.av_malloc(C.size_t(s))) }
func av_malloc(size int) []byte { mem := C.av_malloc(C.uint(size)) data := (*(*[1 << 30]byte)(unsafe.Pointer(mem)))[0:size] return data }
func (self *Decoder) Start(videoStream, audioStream *C.AVStream, scaleWidth, scaleHeight C.int) *Decoder { self.running = true self.duration = time.Duration(self.FormatContext.duration * C.AV_TIME_BASE / 1000) vCodecCtx := videoStream.codec aCodecCtx := audioStream.codec self.durationPerSample = time.Second / time.Duration(aCodecCtx.sample_rate) // frame pool poolSize := 16 pool := make(chan *C.AVFrame, poolSize) self.pool = pool numBytes := C.size_t(C.avpicture_get_size(C.PIX_FMT_YUV420P, scaleWidth, scaleHeight)) for i := 0; i < poolSize; i++ { frame := C.av_frame_alloc() self.frames = append(self.frames, frame) buffer := (*C.uint8_t)(unsafe.Pointer(C.av_malloc(numBytes))) self.buffers = append(self.buffers, buffer) C.avpicture_fill((*C.AVPicture)(unsafe.Pointer(frame)), buffer, C.PIX_FMT_YUV420P, scaleWidth, scaleHeight) pool <- frame } // decode self.frameChan = make(chan *C.AVFrame, 512) go func() { runtime.LockOSThread() // scale context scaleContext := C.sws_getCachedContext(nil, vCodecCtx.width, vCodecCtx.height, vCodecCtx.pix_fmt, scaleWidth, scaleHeight, C.PIX_FMT_YUV420P, C.SWS_LANCZOS, nil, nil, nil) if scaleContext == nil { log.Fatal("get scale context failed") } // resample context resampleContext := C.swr_alloc_set_opts(nil, C.AV_CH_LAYOUT_STEREO, C.AV_SAMPLE_FMT_FLT, aCodecCtx.sample_rate, C.int64_t(aCodecCtx.channel_layout), aCodecCtx.sample_fmt, aCodecCtx.sample_rate, 0, nil) if resampleContext == nil { log.Fatal("get resample context failed") } C.swr_init(resampleContext) var packet C.AVPacket var frameFinished C.int var pts int64 var packetTime time.Duration vFrame := C.av_frame_alloc() aFrame := C.av_frame_alloc() videoIndex := videoStream.index audioIndex := audioStream.index resampleBuffer := (*C.uint8_t)(C.av_malloc(4096 * 8)) resampleBufferp := &resampleBuffer self.Timer = NewTimer() // decode for self.running { // seek if self.seekTarget > 0 { if C.av_seek_frame(self.FormatContext, -1, C.int64_t(float64(self.seekNext)/float64(time.Second)*float64(C.AV_TIME_BASE)), C.AVSEEK_FLAG_BACKWARD) < 0 { log.Fatal("seek error") } for _, codecCtx := range self.openedCodecs { C.avcodec_flush_buffers(codecCtx) } p("frame seek done\n") } read_packet: // read packet C.av_free_packet(&packet) if C.av_read_frame(self.FormatContext, &packet) < 0 { // read packet log.Fatal("read frame error") //TODO stop gracefully } // get packet time if packet.dts != C.AV_NOPTS_VALUE { pts = int64(packet.dts) } else { pts = 0 } if packet.stream_index == videoIndex { packetTime = time.Duration(float64(pts) * float64(C.av_q2d(videoStream.time_base)) * float64(time.Second)) } else if packet.stream_index == audioIndex { packetTime = time.Duration(float64(pts) * float64(C.av_q2d(audioStream.time_base)) * float64(time.Second)) } else { // ignore packet goto read_packet } p("packet time %v at timer time %v\n", packetTime, self.Timer.Now()) // check seek if self.seekTarget > 0 && packetTime > 0 { // if packet time cannot determined, skip if packetTime < self.seekTarget { // seek again self.seekNext += self.seekStep p("seek again %v\n", self.seekNext) } else { // seek ok p("seek ok\n") self.seekTarget = 0 self.Timer.Jump(packetTime - self.Timer.Now()) } } // decode if packet.stream_index == videoIndex { // decode video if C.avcodec_decode_video2(vCodecCtx, vFrame, &frameFinished, &packet) < 0 { continue // bad packet } if frameFinished <= 0 { goto read_packet // frame not complete } bufFrame := <-pool // get frame buffer C.sws_scale(scaleContext, // scale &vFrame.data[0], &vFrame.linesize[0], 0, vCodecCtx.height, &bufFrame.data[0], &bufFrame.linesize[0]) bufFrame.pts = C.int64_t(packetTime) // set packet time self.frameChan <- bufFrame // push to queue p("video frame %v\n", packetTime) } else if packet.stream_index == audioIndex { // decode audio decode_audio_packet: l := C.avcodec_decode_audio4(aCodecCtx, aFrame, &frameFinished, &packet) if l < 0 { continue // bad packet } if frameFinished <= 0 { goto read_packet // frame not complete } if frameFinished > 0 { // frame finished n := C.swr_convert(resampleContext, resampleBufferp, 4096, aFrame.extended_data, aFrame.nb_samples) if n != aFrame.nb_samples { log.Fatal("audio resample failed") } self.audioFrames <- &AudioFrame{ time: packetTime, data: C.GoBytes(unsafe.Pointer(resampleBuffer), n*8), } } if l != packet.size { // multiple frame packet packet.size -= l packet.data = (*C.uint8_t)(unsafe.Pointer(uintptr(unsafe.Pointer(packet.data)) + uintptr(l))) goto decode_audio_packet } p("audio frame %v\n", packetTime) } else { // other stream goto read_packet } } }() // sync video go func() { maxDelta := (time.Second / time.Duration(C.av_q2d(videoStream.r_frame_rate)/10)) for frame := range self.frameChan { delta := time.Duration(frame.pts) - self.Timer.Now() p("video frame %v, delta %v, max delta %v\n", time.Duration(frame.pts), delta, maxDelta) if delta > 0 { if delta > maxDelta { self.Timer.Jump(delta) print("timer jumped\n") } else { time.Sleep(delta) } } else if delta < 0 { // drop frame self.RecycleFrame(frame) continue } self.timedFrames <- frame } }() return self }