// Scale a YUVImage and return the new YUVImage func Scale(src *jpeg.YUVImage, opts ScaleOptions) (*jpeg.YUVImage, error) { // Figure out what format we're dealing with var srcFmt, dstFmt int32 var flags C.int flags = C.SWS_FULL_CHR_H_INT | C.int(opts.Filter) | C.SWS_ACCURATE_RND components := 3 var dst jpeg.YUVImage dstFmt = C.PIX_FMT_YUV444P dst.Format = jpeg.YUV444 switch src.Format { case jpeg.YUV444: srcFmt = C.PIX_FMT_YUV444P flags |= C.SWS_FULL_CHR_H_INP case jpeg.YUV422: srcFmt = C.PIX_FMT_YUV422P case jpeg.YUV440: srcFmt = C.PIX_FMT_YUV440P case jpeg.YUV420: srcFmt = C.PIX_FMT_YUV420P case jpeg.Grayscale: srcFmt = C.PIX_FMT_GRAY8 dstFmt = C.PIX_FMT_GRAY8 components = 1 dst.Format = jpeg.Grayscale } // swscale can't handle images smaller than this; pad them paddedDstWidth := opts.DstWidth paddedSrcWidth := src.Width padFactor := 1 for paddedDstWidth < 8 || paddedSrcWidth < 4 { paddedDstWidth *= 2 paddedSrcWidth *= 2 padFactor *= 2 } // Get the SWS context sws := C.sws_getContext(C.int(paddedSrcWidth), C.int(src.Height), srcFmt, C.int(paddedDstWidth), C.int(opts.DstHeight), dstFmt, flags, nil, nil, nil) if sws == nil { return nil, errors.New("sws_getContext failed") } defer C.sws_freeContext(sws) // We only need 3 planes, but libswscale is stupid and checks the alignment // of all 4 pointers... better give it a dummy one. var srcYUVPtr [4](*uint8) var dstYUVPtr [4](*uint8) var srcStrides [4](C.int) var dstStrides [4](C.int) dst.Width = opts.DstWidth dst.Height = opts.DstHeight dstStride := pad(paddedDstWidth, jpeg.AlignSize) dstFinalPaddedWidth := pad(opts.DstWidth, jpeg.AlignSize) dstPaddedHeight := pad(opts.DstHeight, jpeg.AlignSize) // Allocate image planes and pointers for i := 0; i < components; i++ { dst.Stride[i] = dstStride dst.Data[i] = make([]byte, dstStride*dstPaddedHeight) dstYUVPtr[i] = (*uint8)(unsafe.Pointer(&dst.Data[i][0])) dstStrides[i] = C.int(dstStride) // apply horizontal padding if image is too small if padFactor > 1 { planeWidth := src.PlaneWidth(i) paddedWidth := planeWidth * padFactor planeHeight := src.PlaneHeight(i) paddedStride := pad(paddedWidth, jpeg.AlignSize) newData := make([]uint8, paddedStride*planeHeight) for y := 0; y < planeHeight; y++ { copy(newData[y*paddedStride:], src.Data[i][y*src.Stride[i]:y*src.Stride[i]+planeWidth]) pixel := src.Data[i][y*src.Stride[i]+planeWidth-1] for x := planeWidth; x < paddedWidth; x++ { newData[y*paddedStride+x] = pixel } } srcStrides[i] = C.int(paddedStride) srcYUVPtr[i] = &newData[0] } else { srcStrides[i] = C.int(src.Stride[i]) srcYUVPtr[i] = (*uint8)(unsafe.Pointer(&src.Data[i][0])) } } C.sws_scale(sws, (**C.uint8_t)(unsafe.Pointer(&srcYUVPtr[0])), &srcStrides[0], 0, C.int(src.Height), (**C.uint8_t)(unsafe.Pointer(&dstYUVPtr[0])), &dstStrides[0]) // Replicate the last column and row of pixels as padding, which is typical // behavior prior to JPEG compression for i := 0; i < components; i++ { for y := 0; y < dst.Height; y++ { pixel := dst.Data[i][y*dstStride+dst.Width-1] for x := dst.Width; x < dstFinalPaddedWidth; x++ { dst.Data[i][y*dstStride+x] = pixel } } lastRow := dst.Data[i][dstStride*(dst.Height-1) : dstStride*dst.Height] for y := dst.Height; y < dstPaddedHeight; y++ { copy(dst.Data[i][y*dstStride:], lastRow) } } return &dst, nil }
func (this *SwsCtx) Free() { C.sws_freeContext(this.swsCtx) }
//void sws_freeContext (struct SwsContext *swsContext) //Free the swscaler context swsContext. func Sws_freeContext(ctxt *SwsContext) { C.sws_freeContext((*C.struct_SwsContext)(ctxt)) }
func sws_free_context(ctx *SwsContext) { C.sws_freeContext(ctx.sws) }
func (id *Capture) Close() { C.av_frame_free(&(id.frame)) C.avcodec_close(id.codec) C.avformat_close_input(&(id.context)) C.sws_freeContext(id.sws) }