相关文章推荐
重情义的小马驹  ·  How to fix fatal ...·  8 月前    · 
博学的烤土司  ·  python - Passing ...·  1 年前    · 

最近下载了10多个G的学习视频,结果一播放,发现声音和画面不同步,原本以为大脑能自动调整画面和和画面,勉强能看下去,结果一分钟不到崩溃了,因为声音比画面快了20秒😓;
原本想删了算了,但是下载了几个礼拜才下好,所以只能想想办法

方案一:众所周知,声音的传播速度是 340m/s,找跟长的线,只要把音响放的足够远,理论上是能达到声音和画面同步;果然是学好数理化,走遍天下都不怕啊;实际体验效果我就不尝试了,哈哈🤣

方案二:如果是电脑,同时打开两个视频播放器,一个只听声音,一个静音只看画面;如果是手机,没办法同时打开两个视频播放器,那就只能两个手机了;亲测可用😎

方案三:之前接触过ffmpeg,专门处理音视频流的,查了下果然可以,相关命令如下:

// 音频相对于视频后移20秒(00:00:20),如果前移就是(-00:00:20)
ffmpeg -y -itsoffset 00:00:20 -i 源视频.mp4 -i 源视频.mp4 -map 0:a -map 1:v -vcodec copy -acodec copy -f mp4 -threads 2 -v warning 调整后的视频.mp4
// 用得到的参数:
//		-y 可覆盖,如果文件已存在强制替换;
//		-itsoffset offset 设置以秒为基准的时间偏移,该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了offset秒。 [-]hh:mm:ss[.xxx]的格式也支持
//		-f fmt 强迫采用格式fmt
//		-v 调试信息级别(quiet、panic、fatal、error、warning、info、verbose、debug)
//		选择媒体流,AVI,mkv,mp4等,可以包含不同种类的多个流

二、编程批量调整同步

每个视频都手动敲一行命令太麻烦了,写代码批量生成岂不快哉;废话不多说,直接上代码:

#include <string>
#include <iostream>
#include <vector>
#include <thread>
#include <windows.h>
#include <string.h>
#include <io.h>
#include <direct.h>
#include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
using namespace std;
vector<string> g_vecVideoPaths;
#define ERR_NO_FIND		-1
int FindFile(const char* pszPath)
	int iRet = 0;
	char szFindPath[MAX_PATH] = { 0 };
	DWORD dwFileAttributes;
	string strFileName;
	WIN32_FIND_DATA wfd;
	sprintf(szFindPath, "%s*.*", pszPath);
	HANDLE hFindFile = ::FindFirstFile(szFindPath, &wfd);
	if (hFindFile == INVALID_HANDLE_VALUE)
		// 没有找到任何文件
		return ERR_NO_FIND;
	// 找到文件,开始遍历
	strFileName = wfd.cFileName;
	while (strFileName.size() > 0)
		// 过滤 . 和 ..
		if (strFileName != "." && strFileName != "..")
			dwFileAttributes = wfd.dwFileAttributes;
			if (dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)	// 目录
				// 如果是目录,则继续递归查找
				char szSubFindPath[MAX_PATH] = { 0 };
				sprintf(szSubFindPath, "%s%s\\", pszPath, strFileName.c_str());
				iRet = FindFile(szSubFindPath);
				if (iRet != 0)
					break;
			else if (dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE) // 文件
				// 检查文件是否为指定的类型,测试只能判断mp4文件
				//bool bRet = PathIsContentType(strFileName.c_str(), "video/mp4");
				// 根据文件后缀判断文件是否是视频文件,我只用到*.mp4、*.mkv、*.rmvb
				// 获取文件路径后缀
				const char* pExtension = PathFindExtension(strFileName.c_str());
				if(pExtension != nullptr && 
					(stricmp(pExtension, ".mp4") == 0 || stricmp(pExtension, ".mkv") == 0 || stricmp(pExtension, ".rmvb") == 0))
					// 视频文件找到了,插入video路径
					g_vecVideoPaths.push_back(string(pszPath) + strFileName);
		// 查找下一个文件
		if (!::FindNextFile(hFindFile, &wfd))
			break;
		strFileName = wfd.cFileName;
	::FindClose(hFindFile);
	return iRet;
void GenerateVideo(const char* pPath)
	if (g_vecVideoPaths.size() == 0)
		cout << "查找视频资源失败" << endl;
		return;
	cout << "--------------------开始调整-------------------------" << endl;
	int iAdjustTime = 0;
	cout << "请输入要调整的时间(单位:秒),音频后移为正数,前移为负数:";
	cin >> iAdjustTime;
	char szSavePath[MAX_PATH] = { 0 };
	char szFileName[MAX_PATH] = { 0 };
	char szSaveFileName[MAX_PATH] = { 0 };
	char szCommand[1024] = { 0 };
	char szAdjustTime[32] = {0};
	char* pPrefix = "";
	if (iAdjustTime < 0)
		pPrefix = "-";
		iAdjustTime = -iAdjustTime;
	int iHour = iAdjustTime / 3600;
	int iMinute = (iAdjustTime % 3600) / 60;
	int iSecond = iAdjustTime % 60;
	sprintf(szAdjustTime, "%s%02d:%02d:%02d", pPrefix, iHour, iMinute, iSecond);
	// 执行ffmpeg函数
	auto funAdjust = [](string strCommand)
		system(strCommand.c_str());
	for (int i = 0; i < g_vecVideoPaths.size(); i++)
		// 跳过空
		if (g_vecVideoPaths[i].size() == 0)
			continue;
		memset(szSavePath, 0, sizeof(szSavePath));
		strcpy(szSavePath, g_vecVideoPaths[i].c_str());
		// 获得路径中的文件名
		const char* pFileName = PathFindFileName(szSavePath);
		// PathFindFileName返回的是指向传入参数szSavePath字符串中文件名所在的地址值,必须用其他变量保存
		memset(szFileName, 0, sizeof(szFileName));
		strcpy(szFileName, pFileName);
		// 删除路径后面的文件名以及\\,可以得到一个文件的路径
		PathRemoveFileSpec(szSavePath);
		strcat(szSavePath, "\\output");
		// 创建保存文件夹
		if (_access(szSavePath, 0) == -1)		// 如果文件夹不存在
			_mkdir(szSavePath);
		// 保存文件名
		sprintf(szSaveFileName, "%s\\%s", szSavePath, szFileName);
		// 组装命令
		// ffmpeg -y -itsoffset 00:00:20 -i 源视频.mp4 -i 源视频.mp4 -map 0:a -map 1:v -vcodec copy -acodec copy -f mp4 -threads 2 -v warning 调整后的视频.mp4
		sprintf(szCommand, "%sffmpeg.exe -y -itsoffset %s -i %s -i %s -map 0:a -map 1:v -vcodec copy -acodec copy -f mp4 -threads 2 -v warning %s",
			pPath, szAdjustTime, g_vecVideoPaths[i].c_str(), g_vecVideoPaths[i].c_str(), szSaveFileName);
		cout << szCommand << endl;
		// 使用线程处理
		std::thread t(funAdjust, szCommand);
		t.join();
		cout << i << "	---	" << "输出:" << szSaveFileName << endl;
int main()
	// 获取当前模块(exe)所在路径
	char szModuleFileName[MAX_PATH] = { 0 };
	::GetModuleFileName(NULL, szModuleFileName, MAX_PATH);
	// 查找目录
	char szFindPath[MAX_PATH] = { 0 };
	strcpy(szFindPath, szModuleFileName);
	char *pPos = strrchr(szFindPath, '\\');
	if (pPos == NULL)
		return -1;
	// 截断文件名
	pPos[1] = '\0';
	FindFile(szFindPath);
	GenerateVideo(szFindPath);
	system("pause");
	return 0;

最终生成的程序需要下载的请点击:c++声音和画面同步,免积分下载

之前的3篇博客,实现了视频音频解码读帧,并同步播放,这篇讲将实现视频进度条的功能,实现拖动播放。还是使用教程【3】的代码,需要前3篇教程的,请点击以下链接: 基于FFmpeg视频播放器开发系列教程(三) 基于FFmpeg视频播放器开发系列教程(二) 基于FFmpeg视频播放器开发系列教程(一) 先在Qt Des... [导读]使用FFMPEG转码一年半的时间,遇到了各种情况的音视频同步,下面我们就来根据问题出现的原因,对所遇到的音视频同步做一个分类。使用FFMPEG转码一年半的时间,遇到了各种情况的音视频同步,下面我们就来根据问题出现的原因,对所遇到的音视频同步做一个分类。1. 源本身音视频同步且无法播放这种情况极为罕见,在A客户东方卫视频道转码时遇到过。表现为输出的音视频严重不匹配,录制下来的视频源... FFmpeg 开发系列连载: FFmpeg 开发(01):FFmpeg 编译和集成 FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放 FFmpeg 开发(03):FFmpeg + OpenSLES 实现音频解码播放 FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放 FFmpeg 开发(05):FFmpeg + OpenGLES 实现视频解码播放和视频滤镜 前文中,我们基于 FFmpeg 利用 .. vs版本:vs2017 ffmpeg版本: built with gcc 8.1.0 (x86_64-win32-seh-rev0, Built by MinGW-W64 project) configuration: --arch=x86_64 --disable-debug --enable-shared --disable-static --e 音视频同步简介 从前面的学习可以知道,在一个视频文件中,音频和视频都是单独以一条流的形式存在,互不干扰。那么在播放时根据视频的帧率(Frame Rate)和音频的采样率(Sample Rate)通过简单的计算得到其在某一Frame(Sample)的播放时间分别播放,**理论**上应该是同步的。但是由于机器运行速度,解码效率等等因素影响,很有可能出现音频和视频同步,例如出现视频中人在说话,却只能看到人物嘴动却没有声音,非常影响用户观看体验。 如何做到音视频同步?要知道音视频同步是一个动态的过程,同步是暂 在用解码器解码音频数据得到PCM音频数据块之后,可以在将数据送给声卡播放之前调节其音量大小,具体的实现函数如下: void RaiseVolume(char* buf, UINT32 size, UINT32 uRepeat, double vol)//buf为需要调节音量的音频数据块首地址指针,size为长度,uRepeat为重复次数,通常设为1,vol为增益倍数,可以小于1 if (! C 语言的内存管理,分成两部分。一部分是系统管理的,另一部分是用户手动管理的。 系统管理的内存,主要是函数内部的变量(局部变量)。这部分变量在函数运行时进入内存,函数运行结束后自动从内存卸载。这些变量存放的区域称为”栈“(stack),”栈“所在的内存是系统自动管理的。 用户手动管理的内存,主要是程序运行的整个过程中都存在的变量(全局变量),这些变量需要用户手动从内存释放。如果使用后忘记释放,它就一直占用内存,直到程序退出,这种情况称为”内存泄漏“(memory leak)。这些变量所在的内存称为”堆 我们基于 FFmpeg 利用 OpenGL ES 和 OpenSL ES 分别实现了对解码后视频和音频的渲染,本文将实现播放器的最后一个重要功能:音视频同步。老人们经常说,播放器对音频和视频的播放没有绝对的静态的同步,只有相对的动态的同步,实际上音视频同步就是一个“你追我赶”的过程。音视频同步方式有 3 种,即:音视频分别向系统时钟同步、音频向视频同步视频向音频同步。 第一个视频无声,合并之后整个视频无声 例如上面是我用图片合成的视频,就是没有音频的视频。只要没有音频的视频放在最前面,那么整个视频都会没有声音ffmpeg默认以第一个视频为标准。 解决方法:给第一个视频添加音频 ffmpeg -i 1.mp4 -i a.mp3 -vcodec copy -acodec copy 1.mp4 ps:如果是图片合成的视频,上面命令可能会报错,建议重新合成视频,此时一定得加上音频 ffmpeg -r 15 -f image2 -loop 1 -i 图片.png -i HDMI是很常见的的输入接口,英文全称是High Definition Multimedia Interface,中文即高清多媒体界面,它是一种数字化影像和声音发送接口,可以发送未压缩的音频及视频信号。HDMI可以同时发送音频和视频信号,由于音频和视频信号采用同一条线材,大大简化系统线路的安装难度,其接口呈倒梯形,较偏平。如下图:HDMI的接口分成A、B、C、D4种类型。A类(Type A)是最常... ffmpeg是世界上应用广的开源视频处理框架,在各个平台(windows,linux,嵌入式、Android、IOS)被广泛应用,可以说是做视频相关项目技能qt是目前跨平台项目界面开发的第一选择,本课程基于这两种框架在加上VC++,给大家讲解延时如何开发出一个视频播放器,各个模块如何结合,如何考虑性能问题,如何设计封装类,如何处理各种异常情况。