func Rescale(a, b, c int64) int64 { return int64(C.av_rescale(C.int64_t(a), C.int64_t(b), C.int64_t(c))) }
// ImageWxH returns a screenshot at the ts milliseconds, scaled to the specified width and height. func (g *Generator) ImageWxH(ts int64, width, height int) (image.Image, error) { img := image.NewRGBA(image.Rect(0, 0, width, height)) frame := C.av_frame_alloc() defer C.av_frame_free(&frame) frameNum := C.av_rescale( C.int64_t(ts), C.int64_t(g.streams[g.vStreamIndex].time_base.den), C.int64_t(g.streams[g.vStreamIndex].time_base.num), ) / 1000 if C.avformat_seek_file( g.avfContext, C.int(g.vStreamIndex), 0, frameNum, frameNum, C.AVSEEK_FLAG_FRAME, ) < 0 { return nil, errors.New("can't seek to timestamp") } C.avcodec_flush_buffers(g.avcContext) var pkt C.struct_AVPacket var frameFinished C.int for C.av_read_frame(g.avfContext, &pkt) == 0 { if int(pkt.stream_index) != g.vStreamIndex { C.av_free_packet(&pkt) continue } if C.avcodec_decode_video2(g.avcContext, frame, &frameFinished, &pkt) <= 0 { C.av_free_packet(&pkt) return nil, errors.New("can't decode frame") } C.av_free_packet(&pkt) if frameFinished == 0 || pkt.dts < frameNum { continue } ctx := C.sws_getContext( C.int(g.Width), C.int(g.Height), g.avcContext.pix_fmt, C.int(width), C.int(height), C.PIX_FMT_RGBA, C.SWS_BICUBIC, nil, nil, nil, ) if ctx == nil { return nil, errors.New("can't allocate scaling context") } srcSlice := (**C.uint8_t)(&frame.data[0]) srcStride := (*C.int)(&frame.linesize[0]) dst := (**C.uint8_t)(unsafe.Pointer(&img.Pix)) dstStride := (*C.int)(unsafe.Pointer(&[1]int{img.Stride})) C.sws_scale( ctx, srcSlice, srcStride, 0, g.avcContext.height, dst, dstStride, ) break } return img, nil }