iOS (socket+H264+videoToolbox)回放视频内存增长与播放速度控制

FFmpeg5.0源码阅读——VideoToobox硬件解码 本文描述了FFmpeg中。 阅读详情

iOS (socket+H264+videoToolbox)视频内存增长与播放速度控制
视频播放的时候 控制视频的内存在这张图的消耗速度上
在这里插入图片描述
录像是存储在某台服务器里面的,服务端一般看客户端要多少数据 ,就会开个while循环,一次读取多少字节的数据。然后几乎没有时间间隔的把数据送出去。这时候在播放端就需要做好播放速度的控制,因为接收速度完全取决于网速,如果没有做这段,那么视频的播放速度就和网速一样,网速快的时候 ,视频呈现快进的效果。网速慢的时候会出现卡帧的效果。直播的时候,由于录像采集方控制了采集的速度,所以传输给播放端的时候不需要处理播放速度。

按照标准的H264来说,应该每一帧都有pts时间戳 ,这样就可以做视频的同步来解决问题。

但就有奇葩的没有pts的H264流,该怎么处理这种流呢?

把流下载下来存文件,放到VLC里面 也能够正常播放,那么VLC是怎么做到流速控制,他怎么在没有pts的情况下知道间隔多少毫秒播放呢。用ffmpeg打印文件信息,ffprose就能什么都打印出来,拿到avformat里面的帧率 是1/25 那么就是每秒25帧,就是每隔40毫秒需要渲染一帧视频画面。

如果不是标准的25帧的帧率,那么可以通过avopeninput多等几帧来尝试解析文件,让ffmpeg

帮你猜测帧率,然后再写好等待函数的具体毫秒时间。

至于流播放的过慢应该怎么控制,可以通过一个视频缓冲区来控制,比如我们一秒钟能够保证视频

的流畅体验,那么小于25帧 就不应该播放,应该继续拉流把缓冲区至少填充到25帧,再以间隔40毫秒的速度

播放下去。而缓冲区也不能够无限的大,当视频比如说已经5秒(125帧)了。那就不要再继续拉流了。

这样可以做到流量节省,同时保证流畅的播放体验,也不会造成缓冲区过大的问题。

从上面的内容,我们知道要实现两个东西

视频缓冲区,播放间隔速度控制

在实际编码的过程中

出现了两个比较严重的问题,一个是内存增长过快,到一定程度就会被苹果杀掉了。

一个是数据拷贝的时候数据已经被其他线程释放了,导致读写的时候出现越界崩溃的问题。

还有播放速度无法控制。

针对内存增长过快,原因是拉流缓冲区释放不及时,接收速度远超过播放速度,高人提供了一个解决方案,

就是提前预分配内存,首先分配一个大概2M大小的内存,拉流收到的视频帧直接存到这个内存里面去

控制一个读写的位置,每次的新帧就会一直拼在后面,这样write的位置就会一直往后,read的位置则每读取

一包数据,就向后移动一个位置,当write写到了内存长度的最后的时候,又从头开始写,因为read已经读过了

的部分,就可以覆盖写了,这样包装所有的数据都是在这2M内的,当播放结束的时候,再把2M的内存完全释放。

这样就不用反复分配释放某个内存,而苹果是ARC的,什么时候释放也不是我们能控制的,因此这段代码就C

代码的实现,需要分配和释放成对出现,当然这从某种角度讲,也叫内存池。这里有一套java的实现,

翻译成C的就好了。

package com.temobi.util;


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class VideoBuffer {
	private static final int HEADLEN = 8;
	private static final int MAGIC = 0xabababab;
	
	private Lock lockWrite = new ReentrantLock(); 	// buffer write lock
	private int writePos; 		// write position
	private int readPos; 		// read position
	private int dataLength; 	// data length of the buffer
	private int bufferSize; 	// buffer size
	private byte[] buffer; 		// buffer

	public VideoBuffer(int size) {
		writePos = 0;
		readPos = 0;
		dataLength = 0;
		bufferSize = size;
		buffer = new byte[bufferSize];
	}

	public boolean isHasData() {
		return dataLength > HEADLEN ? true : false;
	}
	
	public int getFreeSpaceSize() {
		return bufferSize - dataLength;
	}

	//splice the buffer to tailed
	public boolean spliceBuffer(VideoBuffer buffer) {
		byte[] buf = null;
		if (!buffer.isHasData()) {
			return false;
		}

		int availableLen = makeRoom(buffer.getDataSize());
		if (availableLen < buffer.getDataSize()) {
			return false;
		}

		int len = buffer.getFirstData(buf);
		if (len > 0) {
			spliceData(buf, len);
			len = buffer.getSecondData(buf);
			if (len > 0) {
				spliceData(buf, len);
			}

			return true;
		}

		return false;
	}

	public void clearBuffer() {
		lockWrite.lock();
		try {
			writePos = 0;
			readPos = 0;
			dataLength = 0;
		} finally {
			lockWrite.unlock();
		}
	}

	public int writeOnePacket(byte[] Data, int len) {
		lockWrite.lock();
		try {
			int packetLen = len + HEADLEN;
			if (bufferSize < packetLen )
				return 0;
			
			if(getFreeSpaceSize() < packetLen) {
				int availableLen = makeRoom(packetLen);
				if (availableLen < packetLen) {
					return 0;
				}
			}
			
			byte[] head = new byte[HEADLEN];
			int2Bytes(head, 0, MAGIC, 4); 	
			int2Bytes(head, 4, packetLen, 4);
			appendData(head, HEADLEN);
			appendData(Data, len);

			return packetLen;
		} finally {
			lockWrite.unlock();
		}
	}

	// read and remove one packet frome buffer
	public int getOnePacket(byte[] outBuf) {
		lockWrite.lock();
		try {
			if (HEADLEN > dataLength) {
				return 0;
			}

			// first, read head from buffer
			byte[] buf = new byte[HEADLEN];
			int rtn = readData(buf, HEADLEN);
			if (rtn == 0)
				return 0;

			// second, parse head to get body length
			int bodyLen = bytes2Int(buf, 4) - HEADLEN;

			// third, read body from buffer
			bodyLen = readData(outBuf, bodyLen);

			return bodyLen;
		} finally {
			lockWrite.unlock();
		}
	}

	public int getFirstData(byte[] buf) {
		int len = 0;
		lockWrite.lock();
		try {
			if (writePos > readPos) {
				//**********************************
				//		^					^
				//	  readPos			   writePos
				//**********************************
				len = dataLength;
				buf = new byte[len];
				System.arraycopy(buffer, readPos, buf, 0, len);
			} else {
				//**********************************
				//		^					^
				//	  writePos			   readPos
				//**********************************
				len = bufferSize - readPos;
				if (len > 0) {
					buf = new byte[len];
					System.arraycopy(buffer, readPos, buf, 0, len);
				}
			}

			return len;
		} finally {
			lockWrite.unlock();
		}
	}

	public int getSecondData(byte[] buf) {
		int len = 0;
		lockWrite.lock();
		try {
			if (writePos < readPos) {
				len = writePos;
				buf = new byte[len];
				System.arraycopy(buffer, 0, buf, 0, len);
			}

			return len;
		} finally {
			lockWrite.unlock();
		}
	}

	public int getDataSize() {
		lockWrite.lock();
		try {
			return dataLength;
		} finally {
			lockWrite.unlock();
		}
	};

	// read and remove data from buffer
	private int readData(byte[] buf, final int dataLen) {
		lockWrite.lock();
		try {
			int firstPart;
			if (dataLen > dataLength)
				return 0;

			if ((firstPart = bufferSize - readPos) < dataLen) {
				System.arraycopy(buffer, readPos, buf, 0, firstPart);
				System.arraycopy(buffer, 0, buf, firstPart, dataLen - firstPart);
			} else {
				System.arraycopy(buffer, readPos, buf, 0, dataLen);
			}
			readPos += dataLen;

			if (readPos >= bufferSize)
				readPos -= bufferSize;

			dataLength -= dataLen;

			return dataLen;
		} finally {
			lockWrite.unlock();
		}
	}

	// read data from buffer but don't remove
	private int readDataNotRemove(byte[] buf, final int dataLen) {
		lockWrite.lock();
		try {
			if (dataLen > dataLength)
				return 0;

			int firstPart;
			if ((firstPart = bufferSize - readPos) < dataLen) {
				System.arraycopy(buffer, readPos, buf, 0, firstPart);
				System.arraycopy(buffer, 0, buf, firstPart, dataLen - firstPart);
			} else {
				System.arraycopy(buffer, readPos, buf, 0, dataLen);
			}

			return dataLen;
		} finally {
			lockWrite.unlock();
		}
	}

	// remove one packet from buffer
	private void removeOnePacket() {
		lockWrite.lock();
		try {
			if (dataLength < HEADLEN)
				return;
			byte[] buf = new byte[HEADLEN];
			int dataLen = readDataNotRemove(buf, HEADLEN);
			dataLen = bytes2Int(buf, 4);
			readPos += dataLen;
			if (readPos >= bufferSize)
				readPos -= bufferSize;

			dataLength -= dataLen;
		} finally {
			lockWrite.unlock();
		}
	}

	// append data to buffer
	private int appendData(byte[] buf, int len) {
		lockWrite.lock();
		try {
			int firstPart = 0;
			if ((firstPart = bufferSize - writePos) < len) {
				System.arraycopy(buf, 0, buffer, writePos, firstPart);
				System.arraycopy(buf, firstPart, buffer, 0, len - firstPart);
			} else {
				System.arraycopy(buf, 0, buffer, writePos, len);
			}

			writePos += len;
			if (writePos >= bufferSize)
				writePos -= bufferSize;

			dataLength += len;

			return len;
		} finally {
			lockWrite.unlock();
		}
	}

	// make room for data limit by length
	private int makeRoom(int length) {
		if(length + HEADLEN <= bufferSize) {
			while (bufferSize - dataLength < length) {
				removeOnePacket();
			}
		}
		return bufferSize - dataLength;
	}

	private void spliceData(byte[] buf, int len) {
		lockWrite.lock();
		try {
			if (writePos >= readPos) {
				int copyLen = bufferSize - writePos;
				if (copyLen < len) {
					System.arraycopy(buf, 0, buffer, writePos, copyLen);
					System.arraycopy(buf, copyLen, buffer, 0, len - copyLen);
					writePos = len - copyLen;
					dataLength += copyLen;
				} else {
					System.arraycopy(buf, 0, buffer, writePos, len);
					writePos += len;
					dataLength += copyLen;
				}
			} else {
				System.arraycopy(buf, 0, buffer, writePos, len);
				writePos += len;
				dataLength += len;
			}
		} finally {
			lockWrite.unlock();
		}
	}

	public static void java_int2C_Bytes(byte[] inByte, int nStart, int value, int len) {
		for (int i = 0; i < len; i++) {
			inByte[i + nStart] = (byte) (value >> 8 * i);
		}
	}

	public static void java_long2C_Bytes(byte[] inByte, int nStart, long value, int len) {
		for (int i = 0; i < len; i++) {
			inByte[i + nStart] = (byte) (value >> 8 * i);
		}
	}

	public static short c_bytes2Java_Short(byte[] b, int start, int len) {
		short sum = 0;
		for (int i = 0; i < len; i++) {
			short n = (short) ((short)b[i + start] & (short)0xff);
			n <<= (i) * 8;
			sum += n;
		}

		return sum;
	}
	
	public static int c_bytes2Java_Int(byte[] b, int start, int len) {
		int sum = 0;
		for (int i = 0; i < len; i++) {
			int n = b[i + start] & 0xff;
			n <<= (i) * 8;
			sum += n;
		}

		return sum;
	}

	public static long c_bytes2Java_long(byte[] b, int start, int len) {
		long sum = 0;
		for (int i = 0; i < start; i++) {
			long n = b[i] & 0xff;
			n <<= (i) * 8;
			sum += n;
		}

		return sum;
	}

	public static void int2Bytes(byte[] inByte, int nStart, int value, int len) {
		int nEnd = nStart + len;
		for (int i = 0; i < len; i++) {
			inByte[nEnd - i - 1] = (byte) (value >> 8 * i);
		}
	}

	public static void long2Bytes(byte[] inByte, int nStart, long value, int len) {
		int nEnd = nStart + len;
		for (int i = 0; i < len; i++) {
			inByte[nEnd - i - 1] = (byte) (value >> 8 * i);
		}
	}
	
	public static short bytes2Short(byte[] b, int start) {
		return (short) (b[1 + start] & 0xFF | (b[0 + start] & 0xFF) << 8);
	}

	public static int bytes2Int(byte[] b, int start) {
		return b[3 + start] & 0xFF | (b[2 + start] & 0xFF) << 8 | (b[1 + start] & 0xFF) << 16
				| (b[0 + start] & 0xFF) << 24;
	}

	public static long bytes2long(byte[] b, int start) {
		return b[7 + start] & 0xFF | (b[6 + start] & 0xFF) << 8 | (b[5 + start] & 0xFF) << 16
				| (b[4 + start] & 0xFF) << 24 | (b[3 + start] & 0xFF) << 32 | (b[2 + start] & 0xFF) << 40
				| (b[1 + start] & 0xFF) << 48 | (b[0 + start] & 0xFF) << 56;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

上面的代码遇到了需要多个线程同时读写的情况, java里面有个ReentrantLock 可重入锁,

iOS中我们常用的是NSLock这种互斥锁,当已经开始写入的时候时候被加了锁,那么读取的时候再

加锁就会造成死锁了。就是不能多次加锁的意思,必须先解锁再加锁,可能有的人会说条件锁

为什么不受影响,但条件锁也是先解锁发了信号量出去再加锁的。最后找到了IOS最通用的锁

同步锁,@synchronized (self) {} 这种锁的效率最低,但是支持锁里面再加锁的。

再说内存共享的问题,为什么要内存共享,因为数据量非常大,一个高清的I帧,有的能到70K

的大小,如果到处做拷贝复制的操作,如果释放不及时,必然会造成内存的增长

socket接收端把数据发给缓冲区的时候,应该是异步拷贝放入缓冲区后立刻释放掉,

防止数据处理影响到socket下一段流的接收速度,播放端播放的时候,应该直接拿缓冲区的数据来播放,

而不是拷贝一份去播放,因为始终只有这个缓冲区的数据存在,而内存又是提前分配的,

就使得内存在一定范围内保持恒定。

应该注意缓冲区不应该过小,这样会导致write过快达到内存大小,然后又从头再写

这时候就会覆盖原来的数据,如果read速度跟不上write写的速度,那么因为数据是共享的,

当read的数据刚读取到要解码的时候,数据已经变了,这样就解析不出来帧或者有一部分数据

缺失导致花帧。因此设计的时候,应该保证缓冲区大小由外部来分配。

通过调整,在网络最快的情况下也能保证read的位置始终慢于write的位置即可。

这是一张改造完成后的内存消耗图,可以看到还是比较稳定的。

然后是播放时间的控制,按照前面的内容所讲,一开始我是开了一个while循环来通过睡眠固定的间隔36毫秒

来保障视频的渲染速度,渲染也要几毫秒,但实际在测试的过程中,发现视频最终的渲染并不是按照40毫秒的

间隔来的,精准时间可以拿CFAbsoluteTimeGetCurrent() 这个函数来看,问了一下,原来是线程的睡眠

时间并不可靠,线程间使用都是在线程在空闲的情况下,再进行下一轮睡眠,而在while循环中,可能还有其他的

内容与函数,一旦有存在等待的情况,就会导致间隔加长,视频渲染就会出现慢放的效果。

正确的写法是使用计时器,而各个平台的计时器不通用,应该用各自平台自己的写法,原因是只有各自系统

清楚有无线程问题导致计时器产生误差,苹果平台的计时器就是NSTimer和GCD两种,NSTimer的计时器误差

在50毫秒以上,而我们的计时器要求精度在36毫秒,就只能选用GCD的时间调用,GCD也是在线程里面的,

不知道是否会受到影响。

最后渲染的部分,记得使用异步串行队列实现,防止阻塞计时器,最后看下播放效果已经控制的还不错了。

目前还有两个问题,在I帧的解码需要消耗很长时间,看上去有70毫秒,不知道有没有办法解决。还有

计时器里面有可能出现没有帧需要等待的情况,这时候再等36毫秒,可能就慢了,等1毫秒,计时器本身

又变成了一个消耗性能的东西,这两点需要优化。其余已经做得很好了。

Videotoolbox在硬解码的时候,应该注意到当sps 和pps发生变化的情况下,需要重新

把init再执行一遍,不然按照旧的sps 和pps 是无法解析视频的。

拉流的内存消耗如果过快,可以考虑是否在同一线程while循环中持续拉流,可能会导致内存无法及时释放,建议拉流的业务和拉流的网络请求类分开线程处理,可以保证拉流的网络数据回调后能够尽快的释放掉

基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP) 首先我们需要明确一点的就是,为什么需要自定义socket协议? ① 客户端与服务器间的相互通信是异步的 ② 理论上,二者都可以任意地发送或者接受数据 ③ 但是实际上,它们应该配合:当client发送时,server接受; 当server发送时,让client去接受 ④ 那么,任何让它们二者配合默契,相互协调呢? ⑤ 这就引出了我们需要的——应用底层协议来解决 ⑥ 这个所谓的协议,实质上就是代码 我们都知道,TCP或者UDP的握手协议(三次握手 三次挥手之类), 不过这些协议都已经被封装在了TC 阅读详情

相关推荐

Unity RTSP流低延迟播放优化实战:从原理到跨平台实现

实时流媒体协议(RTSP)是音视频传输领域的基础协议,广泛应用于监控、直播、远程协作等场景。其工作原理是通过RTP/UDP或TCP传输媒体数据,由客户端解码后渲染显示。在游戏引擎中实现低延迟RTSP播放的核心技术价值在于打通网络接收、硬件解码与GPU渲染之间的高效通路,从而满足AR/VR、无人机图传等对实时性要求极高的交互式应用需求。本文聚焦于在Unity引擎内构建一套从网络层、解码层到渲染层的系统性优化架构,通过平台原生硬件解码器(如MediaCodec、VideoToolbox)与GPU零拷贝纹理上传技

weixin_33674976的博客 736

iOS拉取SRS流媒体服务器的Rtc流

iOS拉取SRS流媒体服务器的Rtc流

m0_60259116的博客 554

Unity跨平台RTSP/RTMP播放器开发:从FFmpeg集成到低延迟渲染实战

流媒体技术是现代音视频应用的核心,其基础在于通过网络协议实时传输编码后的音视频数据。RTSP和RTMP是两种主流的流媒体协议,分别适用于安防监控和直播场景。其技术原理涉及网络传输、编解码和同步渲染等多个环节。在Unity引擎中实现流媒体播放具有重要价值,能够为AR/VR、远程协作、智慧城市等应用提供实时视频能力。通过集成FFmpeg这一多媒体处理库,开发者可以解析协议、解码视频,并利用Unity的渲染管线将数据转换为纹理。针对网络摄像头和直播流的低延迟播放需求,需要优化缓冲区管理、启用硬件解码,并处理好跨平

weixin_34054866的博客 517

ios系统下h264编码的mp4文件无法播放的问题

H.264中有profile(画质级别)和level(用来约束分辨率、帧率和码率)的概念,profile有四种,分别是Baseline Profile(基本画质)、Extended profile(进阶画质)、Main profile(主流画质)和High profile(高级画质),level一般用数字表示,Level越高,视频的码率、分辨率、fps越高。我们可以通过potplayer等播放软件,查看对应的mp4文件的压缩级别,如果压缩级别高于苹果能支持的压缩级别,则会出现ios下无法播放的问题。

HYEHYEHYE的博客 7461

Unity低延迟RTMP推流实战:从渲染到编码的优化全解析

视频编码与流媒体传输是实时音视频应用的核心技术。其原理是将原始图像数据通过编码算法压缩,再通过网络协议传输,以实现高效、实时的内容分发。这项技术的价值在于能够平衡画质、带宽与延迟,广泛应用于直播、远程协作、云游戏等场景。其中,低延迟优化是工程实践中的关键挑战,涉及渲染管线、硬件加速与网络传输的协同。本文聚焦于Unity引擎与RTMP协议的结合,通过AsyncGPUReadback实现GPU异步读取,并利用NVENC硬件编码器进行高效处理,为数字孪生、虚拟演播等需要高实时性的3D内容推送提供了可行的解决方案。

weixin_33910460的博客 359

基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC

server端--编码音视频数据为H264、AAC 这部分花了好多时间,本身就不具备这方面的相关知识,查阅了不少资料,不过关于VideoToolbox和AudioToolbox方面的编码资料寥寥无几,虽然网上搜索结果看似特别多,其实一看 内容也大同小异,建议还是看看官方的文档。

念念不忘,必有回响。 2788

H.264分隔符AUD误用导致iOS设备无法播放H.265视频的问题解决

AUD,全称Access Unit Delimiter,它是H.264接入单元(Access Unit,AU)的分隔符。 AU定义为多个按解码顺序排列的NALU,这些NALU解码正好生成一个图像。AU可以看成压缩视频比特流的基本单位,压缩视频流有多个按顺序排列的AU组成。每个NALU都会属于某个AU单元,压缩视频流的第一个NALU则为第一个AU的第一个NALU。 AUD是个特殊的NALU,它用来分隔AU,虽然AUD在编码标准中是可选的,但有的播放器会要求必须携带AUD,它才能识别一整幅图像,才能正确解码

静影沉璧 2973

iOS (socket+H264+videoToolbox)视频内存增长与播放速度控制

通过对视频流的内存预分配,避免数据传输的多次创建和free操作,从而避免内存的无限攀升问题 参见 https://blog.youkuaiyun.com/github_35041937/article/details/92075305 文章采用Videotoolbox对H264文件进行硬解码,同时保持播放速度为40毫秒数值。

Unity3D集成FFmpeg实现低延迟RTSP视频流播放实战方案

实时流媒体协议(RTSP)是网络摄像头、安防监控等设备进行音视频传输的核心协议,其工作原理基于客户端-服务器模型,通过建立会话并控制媒体流的播放、暂停等操作。在游戏引擎与工业可视化领域,实现低延迟的RTSP流接入具有重要技术价值,它能将实时监控画面无缝融入数字孪生、模拟训练等应用场景,提升系统的真实感与实用性。本文聚焦于通过集成FFmpeg这一多媒体处理框架,在Unity3D中构建一套自主可控的低延迟视频流解决方案,深入探讨了从线程模型设计、缓冲区优化到跨平台编译集成的完整工程实践路径,为开发安防监控可视化

weixin_30410119的博客 687

H264视频文件播放

在ios平台上实现H264视频裸文件的播放。

工业互联网工控安全与工业质检AI大模型建设方案.pptx

工业互联网工控安全与工业质检AI大模型建设方案.pptx

Unity Socket实时传输:多线程与主线程冲突解决方案详解

在游戏开发与实时应用领域,多线程编程是提升性能的关键技术,它允许程序同时执行多个任务。其核心原理在于通过并发执行来充分利用多核CPU资源,避免阻塞主线程。这一技术对于需要处理网络通信、音视频流或高频数据同步的应用至关重要,能显著提升响应速度和用户体验。然而,在Unity引擎中,由于其单线程主导的架构模型,绝大多数Unity API(如操作GameObject、Texture2D等)并非线程安全,直接在其他线程中调用会导致程序崩溃或数据竞争。因此,实现线程间的安全数据传递成为技术难点。常见的解决方案包括使用线

weixin_34194359的博客 494

MRI多序列,多中心数据集预处理--使用FSL-Flirt 配准数据

FLIRT(FMRIB 的线性图像配准工具)是用于线性(仿射)模态内和模态间脑图像配准的全自动稳健且准确的工具。 什么是配准 在这两张图片上,很容易看出它们没有对齐,左边相对右边倾斜,虽然我们人类视觉系统还是能把对应的解剖关系连系在一起,但是我们使用计算机分析时确不能够 因此,我们希望相同的解剖结构对齐,用坐标表示的话,希望他们在相同的坐标上。如下图 对齐这些解剖结构的过程就叫做配准(register) 因此,配准的目的也是使相同的解剖结构在同一个位置,便于分析。可以将CT和MRI配准,看同一结构在两个

Tina姐的博客 1万+

H264之UDP传输码流、画质、延迟理解记录

H264网络传输之UDP码流与图像的相关性 一、H264编码中会有I帧、P帧、B帧的产生。其中I帧为原始帧,此帧编码后的数据很大,其中1080P图像会达到几百至上千Kbit,一般会设置最大码流限制,在限制的同时会影响到图像质量,一般情况下至需要几百Kbit才可以保证图像质量。P帧与B帧均为参考帧,编码后数据较小,一般在十几至几十Kbit。 二、在网络传输过程中,UDP协议不具备可靠性,在网络传输过程中不得不考虑网络传输带宽,与传输效率保证不丢包。Linux下socket传输下UDP协议下单包最多支持6550

gwc_master的博客 4887

海康/大华设备如何接入GB28181平台?最新2022版互联避坑指南

本文详细解析了海康/大华设备接入GB28181平台的最新2022版互联避坑指南,涵盖参数配置、实时视音频点播协议交互及典型故障排查。重点介绍了GB/T28181-2022标准下的设备编码规范、SIP服务器配置及媒体流保活机制,帮助工程团队高效解决设备离线和媒体流中断等问题。

weixin_30733003的博客 685

病理组学分析系列教程1:使用Python-histolab进行病理图像预处理,tiles切片自动生成

病理图像tiles自动生成,WSI图像预处理

qq_43449643的博客 1740

ios视频实现H264硬编码和软编码编译ffmpeg库及环境搭建(附完整demo)

概述 上篇文章我们学习了视频的相关概念及h264编解码的流程,这篇文章我们主要是做代码实现,其内容概要如下: 利用VideoToolBox对实时视频做h264硬编码 ffmpeg 在mac平台安装ffmpeg 简单常用的ffmpeg命令 如何在mac平台编译出ios开发所用的ffmpeg库以及环境搭建 简单介绍ffmpeg库 利用ffmpeg对实时视频做h264软编码 示例代码: h2...

weixin_34004750的博客 1578

智能座舱-语音交互系统

从什么是智能座舱、智能座舱的发展驱动因素、智能座舱的构成要素三个方面梳理我对智能座舱的基础认识之后,为了加深“智能座舱产品入门”课程中语音交互部分知识的理解,我从什么是语音交互、语音交互的底层技术、智能座舱的语音交互等方面,对智能座舱语音交互系统相关的知识进行了梳理与总结。

GUOSA0928的博客 1440

iOS音视频开发,VideoToolBox H264视频硬解码

VideoToolBox 2014年的WWDC大会,苹果发布iOS 8.0,开放了硬编解码的API,也就是VideoToolBox VideoToolbox 一个底层视频硬编解码框架,纯C语言API,可直接访问硬件的编解码器。提供性能极高的硬编硬解,同时也提供存储在 CoreVideo 像素缓冲区的图像进行格式的转换。 这篇文章记录基于VideoToolBox封装一个H264硬解码视频的工具 硬解码优点 速度快,降低解码时间 功耗大幅降低,降低使用App时的电量消耗 Vid

m0_60259116的博客 2928

不止于修改器:用Cheat Engine(CE)的Lua脚本和D3D Hook打造你的游戏辅助界面

本文深入探讨了Cheat Engine(CE)的高级功能,包括Lua脚本自动化和D3D Hook技术,帮助用户从基础内存扫描升级到开发具有专业图形界面的游戏辅助工具。通过实战示例和优化策略,展示了如何创建高效、稳定的游戏辅助系统,并打包为独立训练器。

weixin_28700411的博客 314

从一根USB线缆说起:深入浅出聊聊那些影响信号完整性和EMC性能的‘小细节’(双绞、屏蔽、360度搭接全解析)

本文深入探讨了USB线缆中影响信号完整性和EMC性能的关键细节,包括双绞线绞距、屏蔽层设计和360度搭接工艺。通过实验数据和实用技巧,揭示了这些‘小细节’如何显著影响高频信号质量和电磁兼容性,为工程师提供了从选购到系统设计的全面指南。

weixin_30824479的博客 437

iOS 实现音频流播放器中踩的内存管理相关的坑

    App 作为工具应用,具备语音提醒的能力。语音是调用云服务进行合成的,这样做灵活些,新增提醒不用准备相应的语音文件。云服务合成音频,以流的方式返回到端上,端上使用 Audio Queue 播放。流式播放,可以抽象为生产者-消费者问题。云是我们的生产者,麦克风是我们的消费者。Audio Queue 已经把问题解决一半了,它创建了 Buffer,提供钩子告诉我们Buffer可用,提供接口让我们...

海洋顶端 719

YOLOv13必学核心:SKAttention选择性核注意力机制原理 + 实战全拆解

SKAttention的核心思想来源于人类视觉系统的一个重要特性:根据不同的视觉刺激,人眼能够自适应地调整感受野大小。SKAttention将这一生物学原理转化为深度学习中的注意力机制,通过动态选择和融合不同大小的卷积核,实现了自适应的多尺度特征提取。在人类视觉皮层中,不同的神经元具有不同大小的感受野,而且这些感受野的大小会根据视觉刺激的类型动态调整。本文将深入解析SKAttention模块的设计原理、数学基础、实现细节,以及其在YOLOv13目标检测框架中的集成应用,为读者提供全面而深入的技术理解。

FJN110的博客 96

VideoToolbox硬解码H264流播放

VideoToolBox硬解码H264 对于H264和VideoToolBox还不熟悉的童鞋一定下先看下 H264基础简介 iOS-VideoToolbox硬编码H264 我们先看下demo效果 整体的流程是: 从摄像头获取视频裸数据-&gt;使用VideoToolBox编码成H264-&gt;使用VideoToolBox解码成image-&gt;使用openGLES绘制展示 demo中上面的是实...

weixin_33937913的博客 1338

电机驱动器MP6540H应用设计

/ ... 其他初始化函数(SystemClock_Config, MX_GPIO_Init, MX_TIM1_Init, MX_TIM2_Init)需要你根据STM32CubeMX生成或手动编写。// 假设霍尔传感器引脚是 GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_10。// 低侧U相GPIO引脚。

zhusl6688的博客 656

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

iOS 音视频专栏(一)视频流H264分析以及坑

引言前段时间进新公司,做视频会议,会诊方向的。不采用任何第三方框架,包括推流,拉流,编解码等视频处理,都是自己来编写,除了音频部分要用到webrtc来处理声音降噪,增益等。过程很艰辛,也是踩了无数的坑,总算项目算是整完了。在此,记录一下音视频这块所需要掌握的一些知识点,很多网上能找到的,在这里我就尽量不会太费笔墨。写的不好,轻拍,大家一起进步。 1.视频流 H264 分析网上有很多的对裸流H264

两米阳光的博客 2457

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: ffmpeg mac下命令
下一篇: 安卓ios文件读写的存放
csdn_Damon
博客等级 码龄10年 37粉丝 143原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值