2022-04-24:用go语言重写ffmpeg的muxing.c示例。

news2024/11/23 8:31:30

2022-04-24:用go语言重写ffmpeg的muxing.c示例。

答案2022-04-24:

本程序的大体过程如下:

  1. 打开输出文件并写入头部信息。

  2. 添加音频和视频流,并为每个流创建 AVCodecContext 对象,根据输入格式设置编码器参数,并打开编码器;同时为每个流创建 AVStream 对象,将编码器参数复制到该对象中,并添加该对象到输出文件的媒体流列表中。

  3. 创建 AVFrame 对象并分配内存,用于保存音频和视频数据。对于音频帧,使用正弦函数生成虚假音频数据填充;对于视频帧,使用颜色生成算法生成虚假视频数据填充。

  4. 循环编码音频和视频,直到编码完成为止。对于音频,将虚假音频数据填充到 AVFrame 中,通过 AVCodecContext 进行编码后写入输出文件;对于视频,将虚假视频数据填充到 AVFrame 中,转换成指定像素格式(如果需要),通过 AVCodecContext 进行编码后写入输出文件。

  5. 写入输出文件尾部信息,关闭输出文件,释放相关资源。

总体上,本程序实现了将虚假音频和视频数据编码成指定格式并写入输出文件的功能。其中,步骤 2 的主要作用是为音频和视频流创建必要的对象和参数,以便后续进行编码和写入;步骤 3 和 4 则是生成虚假数据并进行编码和写入的具体实现。

代码见github/moonfdd/ffmpeg-go库。

命令如下:

go run ./examples/internalexamples/muxing/main.go ./out/muxing.mp4
./lib/ffplay ./out/muxing.mp4

golang代码如下:

package main

import (
	"fmt"
	"math"
	"os"
	"unsafe"

	"github.com/moonfdd/ffmpeg-go/ffcommon"
	"github.com/moonfdd/ffmpeg-go/libavcodec"
	"github.com/moonfdd/ffmpeg-go/libavformat"
	"github.com/moonfdd/ffmpeg-go/libavutil"
	"github.com/moonfdd/ffmpeg-go/libswresample"
	"github.com/moonfdd/ffmpeg-go/libswscale"
)

func main() {
	os.Setenv("Path", os.Getenv("Path")+";./lib")
	ffcommon.SetAvutilPath("./lib/avutil-56.dll")
	ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
	ffcommon.SetAvdevicePath("./lib/avdevice-58.dll")
	ffcommon.SetAvfilterPath("./lib/avfilter-56.dll")
	ffcommon.SetAvformatPath("./lib/avformat-58.dll")
	ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
	ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
	ffcommon.SetAvswscalePath("./lib/swscale-5.dll")

	genDir := "./out"
	_, err := os.Stat(genDir)
	if err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(genDir, 0777) //  Everyone can read write and execute
		}
	}
	main0()
}

func main0() (ret ffcommon.FInt) {
	var video_st, audio_st OutputStream
	var filename string
	var fmt0 *libavformat.AVOutputFormat
	var oc *libavformat.AVFormatContext
	var audio_codec, video_codec *libavcodec.AVCodec
	var have_video, have_audio ffcommon.FInt
	var encode_video, encode_audio ffcommon.FInt
	var opt *libavutil.AVDictionary
	var i ffcommon.FInt

	if len(os.Args) < 2 {
		fmt.Printf("usage: %s output_file\nAPI example program to output a media file with libavformat.\nThis program generates a synthetic audio and video stream, encodes and\nmuxes them into a file named output_file.\nThe output format is automatically guessed according to the file extension.\nRaw images can also be output by using '%%d' in the filename.\n\n", os.Args[0])
		return 1
	}

	filename = os.Args[1]
	for i = 2; i+1 < ffcommon.FInt(len(os.Args)); i += 2 {
		if os.Args[i] == "-flags" || os.Args[i] == "-fflags" {
			libavutil.AvDictSet(&opt, os.Args[i+1][1:], os.Args[i+1], 0)
		}
	}

	/* allocate the output media context */
	libavformat.AvformatAllocOutputContext2(&oc, nil, "", filename)
	if oc == nil {
		fmt.Printf("Could not deduce output format from file extension: using MPEG.\n")
		libavformat.AvformatAllocOutputContext2(&oc, nil, "mpeg", filename)
	}
	if oc == nil {
		return 1
	}

	fmt0 = oc.Oformat

	/* Add the audio and video streams using the default format codecs
	 * and initialize the codecs. */
	if fmt0.VideoCodec != libavcodec.AV_CODEC_ID_NONE {
		add_stream(&video_st, oc, &video_codec, fmt0.VideoCodec)
		have_video = 1
		encode_video = 1
	}
	if fmt0.AudioCodec != libavcodec.AV_CODEC_ID_NONE {
		add_stream(&audio_st, oc, &audio_codec, fmt0.AudioCodec)
		have_audio = 1
		encode_audio = 1
	}

	// /* Now that all the parameters are set, we can open the audio and
	//  * video codecs and allocate the necessary encode buffers. */
	if have_video != 0 {
		open_video(oc, video_codec, &video_st, opt)
	}

	if have_audio != 0 {
		open_audio(oc, audio_codec, &audio_st, opt)
	}

	oc.AvDumpFormat(0, filename, 1)

	/* open the output file, if needed */
	if fmt0.Flags&libavformat.AVFMT_NOFILE == 0 {
		ret = libavformat.AvioOpen(&oc.Pb, filename, libavformat.AVIO_FLAG_WRITE)
		if ret < 0 {
			fmt.Printf("Could not open '%s': %s\n", filename,
				libavutil.AvErr2str(ret))
			return 1
		}
	}

	/* Write the stream header, if any. */
	ret = oc.AvformatWriteHeader(&opt)
	if ret < 0 {
		fmt.Printf("Error occurred when opening output file: %s\n",
			libavutil.AvErr2str(ret))
		return 1
	}

	for encode_video != 0 || encode_audio != 0 {
		/* select the stream to encode */
		if encode_video != 0 &&
			(encode_audio == 0 || libavutil.AvCompareTs(video_st.next_pts, video_st.enc.TimeBase,
				audio_st.next_pts, audio_st.enc.TimeBase) <= 0) {
			if write_video_frame(oc, &video_st) == 0 {
				encode_video = 1
			} else {
				encode_video = 0
			}
		} else {
			if write_audio_frame(oc, &audio_st) == 0 {
				encode_audio = 1
			} else {
				encode_audio = 0
			}
		}
	}

	/* Write the trailer, if any. The trailer must be written before you
	 * close the CodecContexts open when you wrote the header; otherwise
	 * av_write_trailer() may try to use memory that was freed on
	 * av_codec_close(). */
	oc.AvWriteTrailer()

	// /* Close each codec. */
	if have_video != 0 {
		close_stream(oc, &video_st)
	}
	if have_audio != 0 {
		close_stream(oc, &audio_st)
	}

	if fmt0.Flags&libavformat.AVFMT_NOFILE == 0 {
		/* Close the output file. */
		libavformat.AvioClosep(&oc.Pb)
	}

	/* free the stream */
	oc.AvformatFreeContext()

	return 0
}

const STREAM_DURATION = 10.0
const STREAM_FRAME_RATE = 25                        /* 25 images/s */
const STREAM_PIX_FMT = libavutil.AV_PIX_FMT_YUV420P /* default pix_fmt */

const SCALE_FLAGS = libswscale.SWS_BICUBIC

// a wrapper around a single output AVStream
type OutputStream struct {
	st  *libavformat.AVStream
	enc *libavcodec.AVCodecContext

	/* pts of the next frame that will be generated */
	next_pts      ffcommon.FInt64T
	samples_count ffcommon.FInt

	frame     *libavutil.AVFrame
	tmp_frame *libavutil.AVFrame

	t, tincr, tincr2 ffcommon.FFloat

	sws_ctx *libswscale.SwsContext
	swr_ctx *libswresample.SwrContext
}

func log_packet(fmt_ctx *libavformat.AVFormatContext, pkt *libavcodec.AVPacket) {
	time_base := &fmt_ctx.GetStream(pkt.StreamIndex).TimeBase

	fmt.Printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
		libavutil.AvTs2str(pkt.Pts), libavutil.AvTs2timestr(pkt.Pts, time_base),
		libavutil.AvTs2str(pkt.Dts), libavutil.AvTs2timestr(pkt.Dts, time_base),
		libavutil.AvTs2str(pkt.Duration), libavutil.AvTs2timestr(pkt.Duration, time_base),
		pkt.StreamIndex)
}

func write_frame(fmt_ctx *libavformat.AVFormatContext, c *libavcodec.AVCodecContext, st *libavformat.AVStream, frame *libavutil.AVFrame) ffcommon.FInt {
	var ret ffcommon.FInt

	// send the frame to the encoder
	ret = c.AvcodecSendFrame(frame)
	if ret < 0 {
		fmt.Printf("Error sending a frame to the encoder: %s\n",
			libavutil.AvErr2str(ret))
		os.Exit(1)
	}

	for ret >= 0 {
		var pkt libavcodec.AVPacket

		ret = c.AvcodecReceivePacket(&pkt)
		if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
			break
		} else if ret < 0 {
			fmt.Printf("Error encoding a frame: %s\n", libavutil.AvErr2str(ret))
			os.Exit(1)
		}

		/* rescale output packet timestamp values from codec to stream timebase */
		pkt.AvPacketRescaleTs(c.TimeBase, st.TimeBase)
		pkt.StreamIndex = uint32(st.Index)

		/* Write the compressed frame to the media file. */
		log_packet(fmt_ctx, &pkt)
		ret = fmt_ctx.AvInterleavedWriteFrame(&pkt)
		pkt.AvPacketUnref()
		if ret < 0 {
			fmt.Printf("Error while writing output packet: %s\n", libavutil.AvErr2str(ret))
			os.Exit(1)
		}
	}

	if ret == libavutil.AVERROR_EOF {
		return 1
	} else {
		return 0
	}
}

/* Add an output stream. */
func add_stream(ost *OutputStream, oc *libavformat.AVFormatContext, codec **libavcodec.AVCodec, codec_id libavcodec.AVCodecID) {
	var c *libavcodec.AVCodecContext
	var i ffcommon.FInt

	/* find the encoder */
	*codec = libavcodec.AvcodecFindEncoder(codec_id)
	if *codec == nil {
		fmt.Printf("Could not find encoder for '%s'\n",
			libavcodec.AvcodecGetName(codec_id))
		os.Exit(1)
	}

	ost.st = oc.AvformatNewStream(nil)
	if ost.st == nil {
		fmt.Printf("Could not allocate stream\n")
		os.Exit(1)
	}
	ost.st.Id = int32(oc.NbStreams) - 1
	c = (*codec).AvcodecAllocContext3()
	if c == nil {
		fmt.Printf("Could not alloc an encoding context\n")
		os.Exit(1)
	}
	ost.enc = c

	switch (*codec).Type {
	case libavutil.AVMEDIA_TYPE_AUDIO:
		if (*codec).SampleFmts != nil {
			c.SampleFmt = (*codec).GetSampleFmt(0)
		} else {
			c.SampleFmt = libavutil.AV_SAMPLE_FMT_FLTP
		}
		c.BitRate = 64000
		c.SampleRate = 44100
		if (*codec).SupportedSamplerates != nil {
			c.SampleRate = (*codec).GetSupportedSamplerate(0)
			for i = 0; (*codec).GetSupportedSamplerate(uint32(i)) != 0; i++ {
				if (*codec).GetSupportedSamplerate(uint32(i)) == 44100 {
					c.SampleRate = 44100
				}
			}
		}
		c.Channels = libavutil.AvGetChannelLayoutNbChannels(c.ChannelLayout)
		c.ChannelLayout = libavutil.AV_CH_LAYOUT_STEREO
		if (*codec).ChannelLayouts != nil {
			c.ChannelLayout = (*codec).GetChannelLayout(0)
			for i = 0; (*codec).GetChannelLayout(uint32(i)) != 0; i++ {
				if (*codec).GetChannelLayout(uint32(i)) == libavutil.AV_CH_LAYOUT_STEREO {
					c.ChannelLayout = libavutil.AV_CH_LAYOUT_STEREO
				}
			}
		}
		c.Channels = libavutil.AvGetChannelLayoutNbChannels(c.ChannelLayout)
		ost.st.TimeBase = libavutil.AVRational{1, c.SampleRate}
		break

	case libavutil.AVMEDIA_TYPE_VIDEO:
		c.CodecId = codec_id

		c.BitRate = 400000
		/* Resolution must be a multiple of two. */
		c.Width = 352
		c.Height = 288
		//     /* timebase: This is the fundamental unit of time (in seconds) in terms
		//      * of which frame timestamps are represented. For fixed-fps content,
		//      * timebase should be 1/framerate and timestamp increments should be
		//      * identical to 1. */
		ost.st.TimeBase = libavutil.AVRational{1, STREAM_FRAME_RATE}
		c.TimeBase = ost.st.TimeBase

		c.GopSize = 12 /* emit one intra frame every twelve frames at most */
		c.PixFmt = STREAM_PIX_FMT
		if c.CodecId == libavcodec.AV_CODEC_ID_MPEG2VIDEO {
			/* just for testing, we also add B-frames */
			c.MaxBFrames = 2
		}
		if c.CodecId == libavcodec.AV_CODEC_ID_MPEG1VIDEO {
			/* Needed to avoid using macroblocks in which some coeffs overflow.
			 * This does not happen with normal video, it just happens here as
			 * the motion of the chroma plane does not match the luma plane. */
			c.MbDecision = 2
		}
		break

	default:
		break
	}

	/* Some formats want stream headers to be separate. */
	if oc.Oformat.Flags&libavformat.AVFMT_GLOBALHEADER != 0 {
		c.Flags |= libavcodec.AV_CODEC_FLAG_GLOBAL_HEADER
	}
}

/**************************************************************/
/* audio output */

func alloc_audio_frame(sample_fmt libavutil.AVSampleFormat,
	channel_layout ffcommon.FUint64T,
	sample_rate, nb_samples ffcommon.FInt) *libavutil.AVFrame {
	frame := libavutil.AvFrameAlloc()
	var ret ffcommon.FInt

	if frame == nil {
		fmt.Printf("Error allocating an audio frame\n")
		os.Exit(1)
	}

	frame.Format = int32(sample_fmt)
	frame.ChannelLayout = channel_layout
	frame.SampleRate = sample_rate
	frame.NbSamples = nb_samples

	if nb_samples != 0 {
		ret = frame.AvFrameGetBuffer(0)
		if ret < 0 {
			fmt.Printf("Error allocating an audio buffer\n")
			os.Exit(1)
		}
	}

	return frame
}

func open_audio(oc *libavformat.AVFormatContext, codec *libavcodec.AVCodec, ost *OutputStream, opt_arg *libavutil.AVDictionary) {
	var c *libavcodec.AVCodecContext
	var nb_samples ffcommon.FInt
	var ret ffcommon.FInt
	var opt *libavutil.AVDictionary

	c = ost.enc

	/* open it */
	libavutil.AvDictCopy(&opt, opt_arg, 0)
	ret = c.AvcodecOpen2(codec, &opt)
	libavutil.AvDictFree(&opt)
	if ret < 0 {
		fmt.Printf("Could not open audio codec: %s\n", libavutil.AvErr2str(ret))
		os.Exit(1)
	}

	/* init signal generator */
	ost.t = 0
	ost.tincr = float32(2 * libavutil.M_PI * 110.0 / float64(c.SampleRate))
	// /* increment frequency by 110 Hz per second */
	ost.tincr2 = float32(2 * libavutil.M_PI * 110.0 / float64(c.SampleRate) / float64(c.SampleRate))

	if c.Codec.Capabilities&libavcodec.AV_CODEC_CAP_VARIABLE_FRAME_SIZE != 0 {
		nb_samples = 10000
	} else {
		nb_samples = c.FrameSize
	}

	ost.frame = alloc_audio_frame(c.SampleFmt, c.ChannelLayout,
		c.SampleRate, nb_samples)
	ost.tmp_frame = alloc_audio_frame(libavutil.AV_SAMPLE_FMT_S16, c.ChannelLayout,
		c.SampleRate, nb_samples)

	/* copy the stream parameters to the muxer */
	ret = ost.st.Codecpar.AvcodecParametersFromContext(c)
	if ret < 0 {
		fmt.Printf("Could not copy the stream parameters\n")
		os.Exit(1)
	}

	/* create resampler context */
	ost.swr_ctx = libswresample.SwrAlloc()
	if ost.swr_ctx == nil {
		fmt.Printf("Could not allocate resampler context\n")
		os.Exit(1)
	}

	// /* set options */
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(ost.swr_ctx)), "in_channel_count", int64(c.Channels), 0)
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(ost.swr_ctx)), "in_sample_rate", int64(c.SampleRate), 0)
	libavutil.AvOptSetSampleFmt(uintptr(unsafe.Pointer(ost.swr_ctx)), "in_sample_fmt", libavutil.AV_SAMPLE_FMT_S16, 0)
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(ost.swr_ctx)), "out_channel_count", int64(c.Channels), 0)
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(ost.swr_ctx)), "out_sample_rate", int64(c.SampleRate), 0)
	libavutil.AvOptSetSampleFmt(uintptr(unsafe.Pointer(ost.swr_ctx)), "out_sample_fmt", c.SampleFmt, 0)
	/* initialize the resampling context */
	ret = ost.swr_ctx.SwrInit()
	if ret < 0 {
		fmt.Printf("Failed to initialize the resampling context\n")
		os.Exit(1)
	}
}

/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and
 * 'nb_channels' channels. */
func get_audio_frame(ost *OutputStream) *libavutil.AVFrame {
	frame := ost.tmp_frame
	var j, i, v ffcommon.FInt
	q := (*ffcommon.FInt16T)(unsafe.Pointer(frame.Data[0]))

	// /* check if we want to generate more frames */
	if libavutil.AvCompareTs(ost.next_pts, ost.enc.TimeBase,
		STREAM_DURATION, libavutil.AVRational{1, 1}) > 0 {
		return nil
	}

	for j = 0; j < frame.NbSamples; j++ {
		v = ffcommon.FInt(math.Sin(float64(ost.t)) * 10000)
		for i = 0; i < ost.enc.Channels; i++ {
			*q = ffcommon.FInt16T(v)
			q = (*ffcommon.FInt16T)(unsafe.Pointer(uintptr(unsafe.Pointer(q)) + 2))
		}
		ost.t += ost.tincr
		ost.tincr += ost.tincr2
	}

	frame.Pts = ost.next_pts
	ost.next_pts += int64(frame.NbSamples)

	return frame
}

/*
 * encode one audio frame and send it to the muxer
 * return 1 when encoding is finished, 0 otherwise
 */
func write_audio_frame(oc *libavformat.AVFormatContext, ost *OutputStream) ffcommon.FInt {
	var c *libavcodec.AVCodecContext
	var frame *libavutil.AVFrame
	var ret ffcommon.FInt
	var dst_nb_samples ffcommon.FInt

	c = ost.enc

	frame = get_audio_frame(ost)

	if frame != nil {
		/* convert samples from native format to destination codec format, using the resampler */
		/* compute destination number of samples */
		dst_nb_samples = int32(libavutil.AvRescaleRnd(ost.swr_ctx.SwrGetDelay(int64(c.SampleRate))+int64(frame.NbSamples),
			int64(c.SampleRate), int64(c.SampleRate), libavutil.AV_ROUND_UP))
		//     av_assert0(dst_nb_samples == frame->nb_samples);

		/* when we pass a frame to the encoder, it may keep a reference to it
		 * internally;
		 * make sure we do not overwrite it here
		 */
		ret = ost.frame.AvFrameMakeWritable()
		if ret < 0 {
			os.Exit(1)
		}

		/* convert to destination format */
		ret = ost.swr_ctx.SwrConvert((**byte)(unsafe.Pointer(&ost.frame.Data)), dst_nb_samples,
			(**byte)(unsafe.Pointer(&frame.Data)), frame.NbSamples)
		if ret < 0 {
			fmt.Printf("Error while converting\n")
			os.Exit(1)
		}
		frame = ost.frame

		frame.Pts = libavutil.AvRescaleQ(int64(ost.samples_count), libavutil.AVRational{1, c.SampleRate}, c.TimeBase)
		ost.samples_count += dst_nb_samples
	}

	return write_frame(oc, c, ost.st, frame)
}

// /**************************************************************/
// /* video output */

func alloc_picture(pix_fmt libavutil.AVPixelFormat, width, height ffcommon.FInt) *libavutil.AVFrame {
	var picture *libavutil.AVFrame
	var ret ffcommon.FInt

	picture = libavutil.AvFrameAlloc()
	if picture == nil {
		return nil
	}

	picture.Format = pix_fmt
	picture.Width = width
	picture.Height = height

	// /* allocate the buffers for the frame data */
	ret = picture.AvFrameGetBuffer(0)
	if ret < 0 {
		fmt.Printf("Could not allocate frame data.\n")
		os.Exit(1)
	}

	return picture
}

func open_video(oc *libavformat.AVFormatContext, codec *libavcodec.AVCodec, ost *OutputStream, opt_arg *libavutil.AVDictionary) {
	var ret ffcommon.FInt
	c := ost.enc
	var opt *libavutil.AVDictionary

	libavutil.AvDictCopy(&opt, opt_arg, 0)

	/* open the codec */
	ret = c.AvcodecOpen2(codec, &opt)
	libavutil.AvDictFree(&opt)
	if ret < 0 {
		fmt.Printf("Could not open video codec: %s\n", libavutil.AvErr2str(ret))
		os.Exit(1)
	}

	/* allocate and init a re-usable frame */
	ost.frame = alloc_picture(c.PixFmt, c.Width, c.Height)
	if ost.frame == nil {
		fmt.Printf("Could not allocate video frame\n")
		os.Exit(1)
	}

	/* If the output format is not YUV420P, then a temporary YUV420P
	 * picture is needed too. It is then converted to the required
	 * output format. */
	ost.tmp_frame = nil
	if c.PixFmt != libavutil.AV_PIX_FMT_YUV420P {
		ost.tmp_frame = alloc_picture(libavutil.AV_PIX_FMT_YUV420P, c.Width, c.Height)
		if ost.tmp_frame == nil {
			fmt.Printf("Could not allocate temporary picture\n")
			os.Exit(1)
		}
	}

	/* copy the stream parameters to the muxer */
	ret = ost.st.Codecpar.AvcodecParametersFromContext(c)
	if ret < 0 {
		fmt.Printf("Could not copy the stream parameters\n")
		os.Exit(1)
	}
}

/* Prepare a dummy image. */
func fill_yuv_image(pict *libavutil.AVFrame, frame_index,
	width, height ffcommon.FInt) {
	var x, y, i ffcommon.FInt

	i = frame_index

	/* Y */
	for y = 0; y < height; y++ {
		for x = 0; x < width; x++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(pict.Data[0])) + uintptr(y*pict.Linesize[0]+x))) = byte((x + y + i*3) % 256)
		}
	}

	// /* Cb and Cr */
	for y = 0; y < height/2; y++ {
		for x = 0; x < width/2; x++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(pict.Data[1])) + uintptr(y*pict.Linesize[1]+x))) = byte((128 + y + i*2) % 256)
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(pict.Data[2])) + uintptr(y*pict.Linesize[2]+x))) = byte((64 + x + i*5) % 256)
		}
	}
}

func get_video_frame(ost *OutputStream) *libavutil.AVFrame {
	c := ost.enc

	/* check if we want to generate more frames */
	if libavutil.AvCompareTs(ost.next_pts, c.TimeBase,
		STREAM_DURATION, libavutil.AVRational{1, 1}) > 0 {
		return nil
	}

	/* when we pass a frame to the encoder, it may keep a reference to it
	 * internally; make sure we do not overwrite it here */
	if ost.frame.AvFrameMakeWritable() < 0 {
		os.Exit(1)
	}

	if c.PixFmt != libavutil.AV_PIX_FMT_YUV420P {
		/* as we only generate a YUV420P picture, we must convert it
		 * to the codec pixel format if needed */
		if ost.sws_ctx == nil {
			ost.sws_ctx = libswscale.SwsGetContext(c.Width, c.Height,
				libavutil.AV_PIX_FMT_YUV420P,
				c.Width, c.Height,
				c.PixFmt,
				SCALE_FLAGS, nil, nil, nil)
			if ost.sws_ctx == nil {
				fmt.Printf("Could not initialize the conversion context\n")
				os.Exit(1)
			}
		}
		fill_yuv_image(ost.tmp_frame, int32(ost.next_pts), c.Width, c.Height)
		ost.sws_ctx.SwsScale((**byte)(unsafe.Pointer(&ost.tmp_frame.Data)),
			(*int32)(unsafe.Pointer(&ost.tmp_frame.Linesize)), 0, uint32(c.Height), (**byte)(unsafe.Pointer(&ost.frame.Data)),
			(*int32)(unsafe.Pointer(&ost.frame.Linesize)))
	} else {
		fill_yuv_image(ost.frame, int32(ost.next_pts), c.Width, c.Height)
	}

	ost.frame.Pts = ost.next_pts
	ost.next_pts++

	return ost.frame
}

/*
 * encode one video frame and send it to the muxer
 * return 1 when encoding is finished, 0 otherwise
 */
func write_video_frame(oc *libavformat.AVFormatContext, ost *OutputStream) ffcommon.FInt {
	return write_frame(oc, ost.enc, ost.st, get_video_frame(ost))
}

func close_stream(oc *libavformat.AVFormatContext, ost *OutputStream) {
	libavcodec.AvcodecFreeContext(&ost.enc)
	libavutil.AvFrameFree(&ost.frame)
	libavutil.AvFrameFree(&ost.tmp_frame)
	ost.sws_ctx.SwsFreeContext()
	libswresample.SwrFree(&ost.swr_ctx)
}

运行结果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/457353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Volatile与ThreadLocal

一&#xff1a;Volatile 线程安全三方面 1 可见性&#xff1a;一个线程对共享变量修改&#xff0c;另一个线程可以看到最新结果 2 有序性&#xff1a; 一个线程内&#xff0c;代码编写按顺序执行 3 原子性&#xff1a; 一个线程内多行代码以一个整体运行&#xff0c;期间不能…

查询网站ip地址

IP地址是Internet Protocol&#xff08;互联网协议&#xff09;的一部分&#xff0c;是一个32位的数字&#xff0c;用于标识网络中的设备。它可以让不同的设备在网络上进行通信和交流&#xff0c;是网络通信的基础。IP地址的应用非常广泛&#xff0c;它可以用于识别和定位设备&…

最新数据, 芯片工程师平均月薪高达2.56W !

近日&#xff0c;据2023年一季度经济运行数据统计&#xff0c;其中提及全国居民人均可支配收入达到10870&#xff0c;同比增长3.8%。 2023年第一季度中&#xff0c;共有20个行业平均月薪超1W&#xff0c;除了大家所熟悉的金融行业薪资水平排名靠前&#xff0c;一些高技术制造业…

iOS SFSpeechRecognizer 语音识别

SFSpeechRecognizer 属于 Speech 框架&#xff0c;在 iOS 10 首次出现&#xff0c;并在 iOS13 中进行了比较重大的更新&#xff0c;在 iOS 13 上支持离线语音识别以及语音分析。WWDC2019 展示了其在 AI 领域的进步&#xff0c;其中 iOS 13 设备内置语音识别就是一项比较不错功能…

第二届中国抗衰老化妆品产业发展论坛暨国粹国妆品牌与文化交流会在京召开

消费日报网讯&#xff08;记者 王儒&#xff09;4月18日&#xff0c;以“国粹国妆 抗衰美丽“为主题的第二届中国抗衰老化妆品产业发展论坛暨国粹国妆品牌与文化交流会在北京召开。大会旨在挖掘国粹力量&#xff0c;搭建沟通交流合作的平台&#xff0c;推动中国高端化妆品品牌建…

网工神器:PNETLab模拟器踩坑过程

目录 0、前言 1、PNETLab介绍 2、下载安装 2.1 下载 2.2 导入 2.3 启动 2.4 注册和登录 3、汉化 4、镜像 5、控制台 5.1、HTML控制台 5.2、默认控制台 6、总结 0、前言 由于工作需要&#xff0c;想测试一下SD-WAN&#xff0c;手边既没有测试环境又没有测试设备。突然想…

八年软件测试生涯,是时候做出改变了

五年前&#xff0c;我在南方的大城市&#xff1a;广州&#xff0c;做着一个快乐的游戏测试&#xff0c;工作不太忙&#xff0c;对一切技术充满了好奇心。测试工作不专业&#xff0c;也不受重视。但我有自己的快乐。工作不忙的时候&#xff0c;我今天学学Python&#xff0c;明天…

uniapp + vue3开发中组合式函数必须是一个同步函数

目录 vue3中的组合式函数用法&#xff1a; 官网示例异步组合式函数&#xff1a;同步函数写法 改造成导出async组合式函数时&#xff1a; uniapp无法使用async组合式函数的原因&#xff1a; vue3中的组合式函数使用时&#xff0c;导出的组合式函数必须是一个同步函数。 vue3…

塔望3W消费战略全案丨元力参堂:从0到1,超级大单品跨越式增长

元力参堂 客户&#xff1a;上海弥富生物科技有限公司 品牌&#xff1a;元力参堂 服务&#xff1a;3W消费战略 品牌全案 项目背景 2020年初&#xff0c;一场突如其来的疫情让我们按下了暂停键&#xff0c;大家经历着疫情的考验。长时间的隔离、封闭影响到生产、消费、投资、物…

ChatGPT3分钟写的千字福尔摩斯小说,老师都分辨不出真假

AI写小说&#xff01;感受ChatGPT3分钟写的千字福尔摩斯小说&#xff01;逆天&#xff01; ChatGPT写悬疑小说 其实在最初&#xff0c;测试者要求ChatGPT写一个5000字的故事&#xff0c;但AI居然直接罢工&#xff0c;还言简意赅&#xff1a;“不&#xff0c;太长了。” 于是…

MySQL库和表的操作

1 什么是数据库&#xff1f;什么是SQL&#xff1f; 科学的组织和存储数据&#xff0c;如何高效获取和维护数据 2 一条SQL语句的执行过程 SQL语句就是一个数据库能够识别的指令语言 在实际操作过程中&#xff0c;创建连接&#xff0c;连接MySQL的server mysql -uroot -P330…

PMP证书备考攻略+PMP知识点汇总

一&#xff0c;考PMP好处多 1.能力提升 大型项目&#xff0c;领导专业团队 2.升职加薪 晋升管理岗&#xff0c;优先升职加薪 3.招投标加分 具有PMP证书&#xff0c;企业招标有加分 4.转型利器 助力转型&#xff0c;拓宽职业发展 5.公司支持 企业鼓励学习&#xff0c;报销费用 6…

LVS负载均衡-DR

1.DR模式中每台主机都有一个VIP地址 虚拟网址放在lo网卡上&#xff08;回环网卡&#xff09; arp_ignore1 Arp_announce2 系统不使用IP包的源地址来设置ARP请求的源地址&#xff0c;而选择发送接口的IP地址 2.内核参数修改 3.vim /etc/rc.conf 开机自启动 Chmod x /etc/rc.d…

Spring Security实战(六)—— 跨域与CORS

跨域是一种浏览器同源安全策略&#xff0c;即浏览器单方面限制脚本的跨域访问。 一、认识跨域 跨域&#xff08;Cross-Origin&#xff09;指的是在Web开发中&#xff0c;当一个网页的内容要从不同源&#xff08;即不同的域名、协议或端口&#xff09;获取时&#xff0c;就会发…

时序分析与时序约束知识总结

文章目录 时序分析如何查看时序报告时序分析的分类和任务HOLD违例修复&#xff1a;SETUP违例修复&#xff1a;时序违例的修复 时序约束约束的分类时序约束的作用SDF文件OCVPVT共同路径悲观效应(CPP)setup time与hold time和什么有关clock Jitter与clock Skewsetup和hold裕度计算…

10.java程序员必知必会类库之邮件

前言 邮件功能在当前互联网应用中已经是很成熟的功能&#xff0c;也是作为java程序员应该掌握的技能。常见使用场景有&#xff1a; 电商软件开电子发票&#xff0c;需要发到用户邮箱里面生产实时报警&#xff0c;需要发到邮箱里面银行软件申请的征信报告&#xff0c;电子账单…

Django框架之Admin站点管理

Django的强大体现在其内置的Admin模块可以使得开发人员在不做任何编码的情况下就拥有网站后台管理功能。 概述 内容发布&#xff1a;负责添加、修改、删除内容 内容访问查看 配置admin应用 在settings.py中添加django.contrib.admin 默认已添加 创建管理员账户 python man…

九、1~8文章的阶段案例

一、案例 现在我们来做一个相对综合一点的练习&#xff1a;书籍购物车 案例说明&#xff1a; 1.在界面上以表格的形式&#xff0c;显示一些书籍的数据&#xff1b;2.在底部显示书籍的总价格&#xff1b;3.点击或者-可以增加或减少书籍数量&#xff08;如果为1&#xff0c;那…

【论文精读】ISBI 2022 - Retinal Vessel Segmentation with Pixel-wise Adaptive Filters

【论文精读】ISBI 2022 - Retinal Vessel Segmentation with Pixel-wise Adaptive Filters 【论文原文】&#xff1a;Retinal Vessel Segmentation with Pixel-wise Adaptive Filters 【作者信息】&#xff1a;Li, Mingxing and Zhou, Shenglong and Chen, Chang and Zhang, …

【Linux】线程-线程控制

线程控制 线程控制线程创建线程终止线程等待分离线程 线程控制 使用线程需要注意的是&#xff0c;需要引入头文件pthread.h&#xff0c;并且在编译的时候&#xff0c;需要使用-lpthread 线程创建 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*…