当前位置: 首页 > article >正文

MFC文件-写MP4

下载本文件
本文件将创作MP4视频文件代码整合到两个文件中(Mp4Writer.h和Mp4Writer.cpp),将IYUV视频流编码为H264,PCM音频流编码为AAC,写入MP4文件。本文件仅适用于MFC程序。

使用方法

1.创建MFC项目。
2.将Mp4Writer.h和Mp4Writer.cpp文件复制到项目目录下。
3.将Mp4Writer.h和Mp4Writer.cpp文件添加到项目。
4.包含Mp4Writer.h头文件,声明Mp4Writer类对象。

#include "Mp4Writer.h"Mp4Writer writer;

5.声明MW_INIT初始化结构,填写结构参数。包括:输出MP4文件路径;视频宽高;视频帧率;视频传输率;音频采样率(仅允许44100和48000采样率);“停止”和“退出”事件句柄。

	HANDLE hStop = CreateEvent(NULL, TRUE, TRUE, NULL);//创建“停止”事件。手动重置HANDLE hExit = CreateEvent(NULL, TRUE, FALSE, NULL);//创建“退出”事件。手动重置MW_INIT MwInit;//writer初始化结构MwInit.Path = L"D:\\1.mp4";//输出文件路径MwInit.VideoWidth = 1024;//视频宽度,单位像素MwInit.VideoHeight = 600;//视频高度,单位像素MwInit.nFramePerSec = 30;//视频帧率MwInit.BIT_RATE = 3072000;//视频传输率MwInit.AudioSamplesPerSec = 48000;//音频采样率MwInit.hStop = hStop;//“停止”事件句柄MwInit.hExit = hExit;//“退出”事件句柄

6.运行。
调用初始化函数创建写视频样本线程和写音频样本线程。在写视频样本线程中创建MP4输出文件,写ftyp box,写mdat box。
反复的调用Mp4Writer类的WriteVideoSample和WriteAudioSample函数,通过函数参数提供视频和音频样本的缓冲区指针和样本大小,函数将样本添加到样本队列。WriteVideoSample和WriteAudioSample函数调用可以同时进行。音频样本的大小应小于或等于1M。
在写视频样本线程中创建H264视频编码器(媒体基础转换);在写音频样本线程中创建AAC音频编码器(媒体基础转换)。设置“停止”无信号后,写视频样本线程将从视频样本队列中读取视频样本,传递给H264编码器,并获取其输出,将输出样本写入mdat box中。写音频样本线程从音频样本队列中读取音频样本,传递给AAC编码器,并获取其输出,将输出样本写入临时文件。

	writer.Init(MwInit);//创建写视频样本线程和写音频样本线程ResetEvent(hStop);//设置“停止”无信号writer.WriteVideoSample(pB, len);//写视频样本writer.WriteAudioSample(pB, len);//写音频样本

7.退出。
同时设置“停止”和“退出”有信号。“停止”信号可以消除队列函数的阻塞,收到“退出”信号后,写音频样本线程首先退出,写视频样本线程等待写音频样本线程退出,音频线程退出后,将包含音频样本的临时文件追加到mdat box视频样本的后面,删除临时文件,写moov box,正确填写各box大小和box参数,关闭MP4输出文件。此时可以获得MP4视频文件。

	SetEvent(hStop);//设置“停止”有信号SetEvent(hExit);//设置“退出”有信号

H264编码器和AAC编码器都有ICodecAPI接口,用于配置编码器。如果需要对编码器进行配置,须在初始化函数调用后,设置“停止”无信号之前。

本文件的使用示例,可以看Windows应用-屏幕录像。

Mp4Writer.h的全部代码

#pragma once#include "mfapi.h"
#include "mftransform.h"
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")
#include "mferror.h"#include "wmcodecdsp.h"
#include "codecapi.h"
#pragma comment(lib, "strmiids")#ifndef  SAFE_RELEASE
#define SAFE_RELEASEtemplate <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}#endif //SAFE_RELEASEstruct MW_INIT
{CString Path;//输出文件路径UINT VideoWidth;//视频图像宽度,单位像素UINT VideoHeight;//视频图像高度,单位像素double nFramePerSec;//视频每秒帧数UINT BIT_RATE;//视频传输率UINT AudioSamplesPerSec;//音频采样率HANDLE hStop = NULL;//“停止”事件句柄HANDLE hExit = NULL;//“退出”事件句柄
};class CQueue//队列
{
public:CQueue(UINT size);~CQueue();BOOL Add(HANDLE hStop, BYTE* pB, LONG len);BOOL Reduce(HANDLE hStop, BYTE*& pB, LONG& len);UINT mSize;BYTE* pBuffer = NULL;BYTE* pAdd = NULL, *pReduce = NULL;HANDLE hAdd = NULL;HANDLE hReduce = NULL;
};class Mp4Writer
{
public:Mp4Writer();~Mp4Writer();MW_INIT mInit;//初始化信息结构HANDLE hFile = NULL;//输出文件句柄HANDLE hAFile = NULL;//音频样本临时文件句柄LONGLONG MdatSizePos;//记录mdat扩展大小位置LONGLONG AudioSampleDur;//音频单个样本时长,单位100纳秒LONGLONG VideoSampleDur;//视频单个样本时长,单位100纳秒UINT32 AvgBytes;//AAC解码器输出传输率CQueue* pVQueue = NULL;//视频样本队列CQueue* pAQueue = NULL;//音频样本队列HANDLE hVThread = NULL;//视频线程句柄HANDLE hAThread = NULL;//音频线程句柄LONGLONG VideoBaseOffset;//视频数据起始位置LONGLONG AudioBaseOffset;//音频数据起始位置WORD SpsSize;//序列参数集大小BYTE* SPS = NULL;//序列参数集WORD PpsSize;//图像参数集大小BYTE* PPS = NULL;//图像参数集CArray<UINT, UINT> VideoKeyFram;//视频关键帧序号数组CArray<UINT, UINT> VideoSizeAry;//视频样本大小数组CArray<UINT, UINT> AudioSizeAry;//音频样本大小数组HANDLE hVideoReady = NULL;//“视频线程初始化完成”事件句柄HANDLE hAudioReady = NULL;//“音频线程初始化完成”事件句柄int GetNaluSize(BYTE* p, UINT len, UINT* pSize, int* pType);//返回NALU单元数量,将单元长度存储在pSize数组中,将单元类型存储在pType数组中void WriteSize(BYTE* p, UINT Size);void GetSpsAndPps(int count, BYTE* pB, LONG len, UINT* pSize, int* pType);HRESULT GetOutput(IMFTransform *pH264Encoder);//获取H264编码器输出void WriteMoov();//写moov boxHRESULT GetAOutput(IMFTransform *pAACEncoder);//获取AAC编码器输出void WriteVideoSample(BYTE* pB, LONG len){if(pVQueue)pVQueue->Add(mInit.hStop, pB, len);}void WriteAudioSample(BYTE* pB, LONG len){if(pAQueue)pAQueue->Add(mInit.hStop, pB, len);}BOOL Init(MW_INIT init);//初始化函数ICodecAPI* pH264API = NULL;//H264编码器设置接口ICodecAPI* pAacAPI = NULL;//AAC编码器设置接口
};

Mp4Writer.cpp的全部代码

#include "stdafx.h"
#include "Mp4Writer.h"CQueue::CQueue(UINT size)
{mSize = size;pBuffer = new BYTE[size * 10];pAdd = pReduce = pBuffer;hAdd = CreateSemaphore(NULL, 0, 10, NULL);//创建“已用”信号量,初始计数0,最大计数10hReduce = CreateSemaphore(NULL, 10, 10, NULL);//创建“可用”信号量,初始计数10,最大计数10
}
CQueue::~CQueue()
{delete[] pBuffer; pBuffer = NULL;CloseHandle(hAdd); CloseHandle(hReduce);
}BOOL CQueue::Add(HANDLE hStop, BYTE* pB, LONG len)
{HANDLE h[2] = { hReduce, hStop };DWORD dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);//“可用”信号量减1,无限期等待if (dw == 1)return FALSE;//如果有“退出”信号,返回CopyMemory(pAdd, &len, 4); pAdd += 4;CopyMemory(pAdd, pB, len); pAdd += mSize - 4;if (pAdd > pBuffer + mSize * 9)pAdd = pBuffer;LONG Pre;ReleaseSemaphore(hAdd, 1, &Pre);//“已用”信号量加1return TRUE;
}BOOL CQueue::Reduce(HANDLE hStop, BYTE*& pB, LONG& len)
{HANDLE h[2] = { hAdd, hStop };DWORD dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);//“已用”信号量减1,无限期等待if (dw == 1)return FALSE;//如果有“退出”信号,返回CopyMemory(&len, pReduce, 4); pReduce += 4;CopyMemory(pB, pReduce, len); pReduce += mSize - 4;if (pReduce > pBuffer + mSize * 9)pReduce = pBuffer;LONG Pre;ReleaseSemaphore(hReduce, 1, &Pre);//“可用”信号量加1return TRUE;
}Mp4Writer::Mp4Writer()
{hVideoReady = CreateEvent(NULL, FALSE, FALSE, NULL);//创建“视频线程初始化完成”事件,自动重置,初始无信号hAudioReady = CreateEvent(NULL, FALSE, FALSE, NULL);//创建“音频线程初始化完成”事件,自动重置,初始无信号
}Mp4Writer::~Mp4Writer()
{CloseHandle(hVideoReady); CloseHandle(hAudioReady);
}void Write1(HANDLE hFile, BYTE byte)
{WriteFile(hFile, &byte, 1, NULL, NULL);
}void Write2(HANDLE hFile, WORD w)
{BYTE hi = (BYTE)((w & 0xFF00) >> 8); BYTE lo = (BYTE)(w & 0xFF);WriteFile(hFile, &hi, 1, NULL, NULL); WriteFile(hFile, &lo, 1, NULL, NULL);
}void Write3(HANDLE hFile, UINT u)
{BYTE mByte1, mByte2, mByte3;mByte1 = (BYTE)((u & 0xFF0000) >> 16); mByte2 = (BYTE)((u & 0xFF00) >> 8); mByte3 = (BYTE)(u & 0xFF);WriteFile(hFile, &mByte1, 1, NULL, NULL); WriteFile(hFile, &mByte2, 1, NULL, NULL); WriteFile(hFile, &mByte3, 1, NULL, NULL);
}void Write4(HANDLE hFile, UINT u)
{BYTE mByte1, mByte2, mByte3, mByte4;mByte1 = (BYTE)((u & 0xFF000000) >> 24); mByte2 = (BYTE)((u & 0xFF0000) >> 16); mByte3 = (BYTE)((u & 0xFF00) >> 8); mByte4 = (BYTE)(u & 0xFF);WriteFile(hFile, &mByte1, 1, NULL, NULL); WriteFile(hFile, &mByte2, 1, NULL, NULL); WriteFile(hFile, &mByte3, 1, NULL, NULL); WriteFile(hFile, &mByte4, 1, NULL, NULL);
}void Write8(HANDLE hFile, ULONGLONG ul)
{BYTE mByte1, mByte2, mByte3, mByte4, mByte5, mByte6, mByte7, mByte8;mByte1 = (BYTE)((ul & 0xFF00000000000000) >> 56); mByte2 = (BYTE)((ul & 0xFF000000000000) >> 48); mByte3 = (BYTE)((ul & 0xFF0000000000) >> 40);mByte4 = (BYTE)((ul & 0xFF00000000) >> 32);mByte5 = (BYTE)((ul & 0xFF000000) >> 24); mByte6 = (BYTE)((ul & 0xFF0000) >> 16); mByte7 = (BYTE)((ul & 0xFF00) >> 8); mByte8 = (BYTE)(ul & 0xFF);WriteFile(hFile, &mByte1, 1, NULL, NULL); WriteFile(hFile, &mByte2, 1, NULL, NULL); WriteFile(hFile, &mByte3, 1, NULL, NULL); WriteFile(hFile, &mByte4, 1, NULL, NULL);WriteFile(hFile, &mByte5, 1, NULL, NULL); WriteFile(hFile, &mByte6, 1, NULL, NULL); WriteFile(hFile, &mByte7, 1, NULL, NULL); WriteFile(hFile, &mByte8, 1, NULL, NULL);
}LONGLONG GetFilePos(HANDLE hFile)//获取文件当前位置
{LARGE_INTEGER move;move.QuadPart = 0;LARGE_INTEGER CUR;SetFilePointerEx(hFile, move, &CUR, FILE_CURRENT);return CUR.QuadPart;
}HRESULT WriteEight(HANDLE hFile, LONGLONG Pos, ULONGLONG* p)//在指定位置写入8字节,并将文件指针返回到原来的位置
{LONGLONG Cur = GetFilePos(hFile);//获取当前位置LARGE_INTEGER Move;Move.QuadPart = Pos;SetFilePointerEx(hFile, Move, NULL, FILE_BEGIN);//移动到指定位置Write8(hFile, *p);//写入8字节Move.QuadPart = Cur;SetFilePointerEx(hFile, Move, NULL, FILE_BEGIN);//返回到原来的位置return S_OK;
}DWORD WINAPI VideoWriterThread(LPVOID lp)
{Mp4Writer* pMp4Writer = (Mp4Writer*)lp;pMp4Writer->hFile = CreateFile(pMp4Writer->mInit.Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建输出文件if (INVALID_HANDLE_VALUE == pMp4Writer->hFile){MessageBox(0, L"创建输出文件失败", L"写MP4", MB_OK);pMp4Writer->hFile = NULL;return 0;}HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础if (hr != S_OK){MessageBox(NULL, L"初始化媒体基础失败", L"写MP4", MB_OK); return 0;}IMFTransform *pH264Encoder = NULL;GUID CLSID_H264EncoderMft = { 0x6ca50344, 0x051a, 0x4ded, 0x97, 0x79, 0xa4, 0x33, 0x05, 0x16, 0x5e, 0x35 };//H264视频编码器的类标识符hr = CoCreateInstance(CLSID_H264EncoderMft, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pH264Encoder));//创建H264视频编码器(媒体基础转换)if (hr != S_OK){MessageBox(NULL, L"H264编码器创建失败", L"写MP4", MB_OK); return 0;}hr = pH264Encoder->QueryInterface(IID_ICodecAPI, (void**)&pMp4Writer->pH264API);//查询H264编码器ICodecAPI接口IMFMediaType *pOutType = NULL;//输出媒体类型hr = MFCreateMediaType(&pOutType);//创建空的媒体类型hr = pOutType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型视频hr = pOutType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);//设置子类型H264if (pMp4Writer->mInit.BIT_RATE == 0){pMp4Writer->mInit.BIT_RATE = (UINT32)((double)pMp4Writer->mInit.VideoWidth * (double)pMp4Writer->mInit.VideoHeight * (double)pMp4Writer->mInit.nFramePerSec / 6);}hr = pOutType->SetUINT32(MF_MT_AVG_BITRATE, (UINT32)pMp4Writer->mInit.BIT_RATE);//设置传输率hr = MFSetAttributeRatio(pOutType, MF_MT_FRAME_RATE, (UINT32)pMp4Writer->mInit.nFramePerSec, 1);//设置帧速率hr = MFSetAttributeSize(pOutType, MF_MT_FRAME_SIZE, (UINT32)pMp4Writer->mInit.VideoWidth, (UINT32)pMp4Writer->mInit.VideoHeight);//设置帧宽高hr = pOutType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);//设置交错模式,无交错hr = pOutType->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);//设置配置文件hr = MFSetAttributeRatio(pOutType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);//设置宽高比hr = pH264Encoder->SetOutputType(NULL, pOutType, 0);//设置H264视频编码器输出媒体类型SafeRelease(&pOutType);if (hr != S_OK){MessageBox(NULL, L"H264编码器设置输出媒体类型失败", L"写MP4", MB_OK); SafeRelease(&pH264Encoder); SafeRelease(&pMp4Writer->pH264API);MFShutdown();//关闭媒体基础return 0;}IMFMediaType* pInType = NULL;hr = MFCreateMediaType(&pInType);//创建空的媒体类型hr = pInType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型视频hr = pInType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV);//设置编码器子类型hr = pInType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, 1);//样本固定大小hr = pInType->SetUINT32(MF_MT_SAMPLE_SIZE, (UINT32)(pMp4Writer->mInit.VideoWidth * pMp4Writer->mInit.VideoHeight * 1.5));//样本大小hr = pInType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1);//样本独立于其他样本hr = MFSetAttributeSize(pInType, MF_MT_FRAME_SIZE, (UINT32)pMp4Writer->mInit.VideoWidth, (UINT32)pMp4Writer->mInit.VideoHeight);//设置帧宽高hr = MFSetAttributeRatio(pInType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);//设置宽高比hr = MFSetAttributeRatio(pInType, MF_MT_FRAME_RATE, (UINT32)pMp4Writer->mInit.nFramePerSec, 1);//设置帧速率hr = pInType->SetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32)pMp4Writer->mInit.VideoWidth);//设置步幅hr = pInType->SetUINT32(MF_MT_AVG_BITRATE, (UINT32)(pMp4Writer->mInit.VideoWidth * pMp4Writer->mInit.VideoHeight * pMp4Writer->mInit.nFramePerSec * 1.5));//设置传输率		hr = pInType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);//交错模式,无交错hr = pH264Encoder->SetInputType(NULL, pInType, 0);//设置H264视频编码器输入媒体类型SafeRelease(&pInType);if (hr != S_OK){MessageBox(NULL, L"H264编码器设置输入媒体类型失败", L"写MP4", MB_OK);SafeRelease(&pH264Encoder); SafeRelease(&pMp4Writer->pH264API);MFShutdown();//关闭媒体基础return 0;}pMp4Writer->pVQueue = new CQueue(pMp4Writer->mInit.VideoWidth * pMp4Writer->mInit.VideoHeight * 4 + 4);//创建队列,最多10个样本LONG len = (LONG)(pMp4Writer->mInit.VideoWidth * pMp4Writer->mInit.VideoHeight*1.5);BYTE* pS = new BYTE[len];int index = 0; double VideoTimePerFrame = (double)10000000 / pMp4Writer->mInit.nFramePerSec;Write4(pMp4Writer->hFile, 24);//ftyp box大小WriteFile(pMp4Writer->hFile, "ftyp", 4, NULL, NULL);//ftyp box标识WriteFile(pMp4Writer->hFile, "isom", 4, NULL, NULL);//文件规范Write4(pMp4Writer->hFile, 1);//版本号WriteFile(pMp4Writer->hFile, "isomavc1", 8, NULL, NULL);//兼容规范Write4(pMp4Writer->hFile, 1);//mdat box大小WriteFile(pMp4Writer->hFile, "mdat", 4, NULL, NULL);//mdat box标识LONGLONG MdatSizePos = GetFilePos(pMp4Writer->hFile);//获取扩展mdat大小位置Write8(pMp4Writer->hFile, (ULONGLONG)0);//写扩展mdat大小,此时未指定实际值Write8(pMp4Writer->hFile, (ULONGLONG)0);//写扩展mdat属性pMp4Writer->VideoBaseOffset = GetFilePos(pMp4Writer->hFile);//记录视频数据起始位置SetEvent(pMp4Writer->hVideoReady);//发送“视频线程初始化完成”信号Agan:DWORD mExit = WaitForSingleObject(pMp4Writer->mInit.hExit, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{WaitForSingleObject(pMp4Writer->hAThread, INFINITE);//等待音频线程退出pMp4Writer->AudioBaseOffset = GetFilePos(pMp4Writer->hFile);//记录音频数据起始位置CloseHandle(pMp4Writer->hFile); CloseHandle(pMp4Writer->hAFile);pMp4Writer->hFile = CreateFile(pMp4Writer->mInit.Path, FILE_APPEND_DATA | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);pMp4Writer->hAFile = CreateFile(L"音频样本临时文件.dat", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);BYTE buff[4096]; DWORD  dwBytesRead, dwBytesWritten, lo; LONG hi;while (ReadFile(pMp4Writer->hAFile, buff, sizeof(buff), &dwBytesRead, NULL) && dwBytesRead > 0)//将临时文件追加到输出文件末尾{hi = 0;lo = SetFilePointer(pMp4Writer->hFile, 0, &hi, FILE_END);//移动文件指针到末尾LockFile(pMp4Writer->hFile, lo, (DWORD)hi, dwBytesRead, 0);WriteFile(pMp4Writer->hFile, buff, dwBytesRead, &dwBytesWritten, NULL);UnlockFile(pMp4Writer->hFile, lo, (DWORD)hi, dwBytesRead, 0);}CloseHandle(pMp4Writer->hAFile);DeleteFile(L"音频样本临时文件.dat");ULONGLONG mdat_largSize = GetFilePos(pMp4Writer->hFile) - 24;//获取扩展mdat大小WriteEight(pMp4Writer->hFile, MdatSizePos, &mdat_largSize);//写扩展mdat大小,并将文件指针返回到当前位置pMp4Writer->WriteMoov();//写moov boxCloseHandle(pMp4Writer->hFile);//关闭输出文件SafeRelease(&pMp4Writer->pH264API); SafeRelease(&pH264Encoder);delete pMp4Writer->pVQueue; pMp4Writer->pVQueue = NULL; delete[] pS; pS = NULL;if (pMp4Writer->SPS){delete[] pMp4Writer->SPS; pMp4Writer->SPS = NULL; pMp4Writer->SpsSize = 0;}if (pMp4Writer->PPS){delete[] pMp4Writer->PPS; pMp4Writer->PPS = NULL; pMp4Writer->PpsSize = 0;}MFShutdown();//关闭媒体基础return 1;}DWORD mStop = WaitForSingleObject(pMp4Writer->mInit.hStop, 0);if (mStop != WAIT_OBJECT_0)//如果“停止”无信号{BOOL BReduce = pMp4Writer->pVQueue->Reduce(pMp4Writer->mInit.hStop, pS, len);//从队列读取样本if (BReduce)//如果读取样本成功{CreateBuffer:IMFMediaBuffer* pMFBuffer = NULL;hr = MFCreateMemoryBuffer(len, &pMFBuffer);//创建媒体基础缓冲区if (hr != S_OK || pMFBuffer == NULL)//如果创建失败{Sleep(1); goto CreateBuffer;//再次创建}BYTE* pData = NULL;hr = pMFBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区CopyMemory(pData, pS, len);//复制数据到媒体基础样本缓冲区hr = pMFBuffer->Unlock();//解锁媒体基础缓冲区hr = pMFBuffer->SetCurrentLength((DWORD)len);//设置媒体基础缓冲区的数据长度CreateSample:IMFSample* pMFSample = NULL;if (SUCCEEDED(hr)){hr = MFCreateSample(&pMFSample);//创建媒体基础样本if (hr != S_OK || pMFSample == NULL)//如果创建失败{Sleep(1); goto CreateSample;//再次创建}}hr = pMFSample->AddBuffer(pMFBuffer);//添加缓冲区到媒体基础样本if (hr == S_OK){LONGLONG star = (LONGLONG)((double)index * VideoTimePerFrame);hr = pMFSample->SetSampleTime(star);//设置媒体基础样本显示时间hr = pMFSample->SetSampleDuration((LONGLONG)VideoTimePerFrame);//设置媒体基础样本持续时间hr = pMFSample->SetUINT32(MFSampleExtension_CleanPoint, (UINT32)TRUE);//设置为关键帧}index++;RePut:hr = pH264Encoder->ProcessInput(NULL, pMFSample, 0);//向编码器传递输入数据if (hr == S_OK)//如果传递输入成功{SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本goto Agan;//继续下一次传递输入}if (MF_E_NOTACCEPTING == hr)//如果不可以传递输入。MF_E_NOTACCEPTING表示已不能接收更多输入{pMp4Writer->GetOutput(pH264Encoder);//获取编码器输出}goto RePut;//传递输入失败时,需将此次的样本再次传递到输入}}goto Agan;
}int Mp4Writer::GetNaluSize(BYTE* p, UINT len, UINT* pSize, int* pType)//返回NALU单元数量,将单元长度存储在pSize数组中,将单元类型存储在pType数组中
{BYTE by[4]; p += 4; len -= 4; int NuIndex = 0; UINT size = 0;BYTE byte;CopyMemory(&byte, p, 1);byte &= 31;pType[NuIndex] = byte;while (len > 0){CopyMemory(by, p, 4);if (by[0] == 0 && by[1] == 0 && by[2] == 0 && by[3] == 1){p += 4; len -= 4;pSize[NuIndex] = size;NuIndex++;size = 0;CopyMemory(&byte, p, 1);byte &= 31;pType[NuIndex] = byte;continue;}p += 1; size += 1; len -= 1;}pSize[NuIndex] = size;return NuIndex + 1;
}void Mp4Writer::WriteSize(BYTE* p, UINT Size)
{BYTE pB1[4]; BYTE pB2[24];CopyMemory(pB1, &Size, 4);pB2[0] = pB1[3]; pB2[1] = pB1[2]; pB2[2] = pB1[1]; pB2[3] = pB1[0];CopyMemory(p, pB2, 4);
}void Mp4Writer::GetSpsAndPps(int count, BYTE* pB, LONG len, UINT* pSize, int* pType)
{BYTE* p = pB;for (int i = 0; i < count; i++){p += 4;if (pType[i] == 7)//单元类型SPS{SpsSize = pSize[i];SPS = new BYTE[SpsSize];CopyMemory(SPS, p, SpsSize);}if (pType[i] == 8)//单元类型PPS{PpsSize = pSize[i];PPS = new BYTE[PpsSize];CopyMemory(PPS, p, PpsSize);}p += pSize[i];}
}HRESULT Mp4Writer::GetOutput(IMFTransform *pH264Encoder)//获取编码器输出
{
CreateOutBuffer:IMFMediaBuffer* pMFOutBuffer = NULL; DWORD BufferSize = mInit.VideoWidth* mInit.VideoHeight * 4;HRESULT hr = MFCreateMemoryBuffer(BufferSize, &pMFOutBuffer);//创建输出媒体基础缓冲区if (hr != S_OK || pMFOutBuffer == NULL)//如果创建失败{Sleep(1); goto CreateOutBuffer;//再次创建}
CreateOutSample:IMFSample* pMFOutSample = NULL;if (SUCCEEDED(hr)){hr = MFCreateSample(&pMFOutSample);//创建输出媒体基础样本if (hr != S_OK || pMFOutSample == NULL)//如果创建失败{Sleep(1); goto CreateOutSample;//再次创建}}hr = pMFOutSample->AddBuffer(pMFOutBuffer);//添加缓冲区到媒体基础样本MFT_OUTPUT_DATA_BUFFER OD;OD.dwStreamID = NULL;OD.pSample = pMFOutSample;//须为编码器指定输出样本OD.dwStatus = 0;OD.pEvents = NULL;DWORD status = 0;hr = pH264Encoder->ProcessOutput(0, 1, &OD, &status);//获取编码器输出数据。数据将输出到刚才创建的输出样本的缓冲区内if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)//如果MFT需要更多的输入数据,此时已不可获取输出{SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样return S_OK;}if (hr == S_OK)//如果成功获得输出数据{LONGLONG s, d;hr = pMFOutSample->GetSampleTime(&s);//获取编码器输出样本开始时间hr = pMFOutSample->GetSampleDuration(&d);//获取编码器输出样本持续时间VideoSampleDur = d;UINT32 KeyFrame;hr = pMFOutSample->GetUINT32(MFSampleExtension_CleanPoint, &KeyFrame);DWORD L;hr = pMFOutSample->GetTotalLength(&L);//获取编码器输出样本有效数据长度BYTE* pD = NULL;hr = pMFOutBuffer->Lock(&pD, NULL, NULL);VideoSizeAry.Add((UINT)L);//将样本大小添加到数组if (KeyFrame)//如果是关键帧{VideoKeyFram.Add((UINT)VideoSizeAry.GetCount());//将关键帧序号添加到数组}UINT pUint[16]; int pType[16];//下面代码将所有“0001”起始码替换为NALU单元大小int count = GetNaluSize(pD, L, pUint, pType);if (SPS == NULL)//第1帧样本包含SPS和PPS{GetSpsAndPps(count, pD, L, pUint, pType);//获取序列参数集和图片参数集}BYTE* p = pD;for (int i = 0; i < count; i++){WriteSize(p, pUint[i]); p += 4 + pUint[i];}WriteFile(hFile, pD, L, NULL, NULL);//写视频样本到文件hr = pMFOutBuffer->Unlock();}SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样goto CreateOutBuffer;//再次获取输出,直到不可获取为止
}void Mp4Writer::WriteMoov()//写moov box
{UINT co64Enter = (UINT)VideoSizeAry.GetCount() / 100;UINT LastEnter = (UINT)VideoSizeAry.GetCount() % 100;if (LastEnter)co64Enter += 1;UINT avcC_size = 16 + SpsSize + 3 + PpsSize;UINT avc1_size = 86 + avcC_size;UINT stsd_size = 16 + avc1_size;UINT stts_size = 24;UINT stss_size = (UINT)VideoKeyFram.GetCount() * 4 + 16;UINT stsc_size = 16 + 12 * 2;UINT stsz_size = (UINT)VideoSizeAry.GetCount() * 4 + 20;UINT co64_size = 16 + co64Enter * 8;UINT vmhd_size = 20;UINT dinf_size = 36;UINT stbl_size = 8 + stsd_size + stts_size + stss_size + stsc_size + stsz_size + co64_size;UINT mdhd_size = 32 + 12;UINT hdlr_size = 38;UINT minf_size = 8 + vmhd_size + dinf_size + stbl_size;UINT tkhd_size = 92;UINT mdia_size = 8 + mdhd_size + hdlr_size + minf_size;UINT video_trak_size = 8 + tkhd_size + mdia_size;UINT mvhd_size = 108;//以上为视频box大小UINT Aco64Enter = (UINT)AudioSizeAry.GetCount() / 100;UINT ALastEnter = (UINT)AudioSizeAry.GetCount() % 100;if (ALastEnter)Aco64Enter += 1;UINT audio_stsd_size = 91;UINT audio_stts_size = 24;UINT audio_stsc_size = 16 + 12 * 2;UINT audio_stsz_size = 20 + (UINT)AudioSizeAry.GetCount() * 4;UINT audio_co64_size = 16 + Aco64Enter * 8;UINT audio_smhd_size = 16;UINT audio_dinf_size = 36;UINT audio_stbl_size = 8 + audio_stsd_size + audio_stts_size + audio_stsc_size + audio_stsz_size + audio_co64_size;UINT audio_mdhd_size = 44;UINT audio_hdlr_size = 38;UINT audio_minf_size = 8 + audio_smhd_size + audio_dinf_size + audio_stbl_size;UINT audio_mdia_size = 8 + audio_mdhd_size + audio_hdlr_size + audio_minf_size;UINT audio_tkhd_size = 92;UINT audio_trak_size = 8 + audio_tkhd_size + audio_mdia_size;//以上为音频box大小UINT moov_size = 8 + mvhd_size + video_trak_size + audio_trak_size;Write4(hFile, moov_size);//moov box大小WriteFile(hFile, "moov", 4, NULL, NULL);//moov box标识Write4(hFile, mvhd_size);//mvhd box大小WriteFile(hFile, "mvhd", 4, NULL, NULL);//mvhd box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)0);//创建时间Write4(hFile, (UINT)0);//修改时间UINT mvhd_time_scale = 600;Write4(hFile, mvhd_time_scale);//时间刻度分母UINT mvhd_duration_time = (UINT)((double)VideoSampleDur *(double)VideoSizeAry.GetCount() / (double)10000000 * (double)mvhd_time_scale);Write4(hFile, mvhd_duration_time);//时间长度Write4(hFile, 65536);//推荐播放速度Write2(hFile, 256);//音量BYTE mvhd_reserved[10] = { 0,0,0,0,0,0,0,0,0,0 };WriteFile(hFile, mvhd_reserved, 10, NULL, NULL);//保留BYTE matrix[36] = { 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0 };WriteFile(hFile, matrix, 36, NULL, NULL);//视频变换矩阵BYTE mvhd_PreDefined[24] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };WriteFile(hFile, mvhd_PreDefined, 24, NULL, NULL);//预定义Write4(hFile, 2);//下一个track使用的id号//视频trakWrite4(hFile, video_trak_size);//trak box大小WriteFile(hFile, "trak", 4, NULL, NULL);//trak box标识Write4(hFile, tkhd_size);//tkhd box大小WriteFile(hFile, "tkhd", 4, NULL, NULL);//tkhd box标识Write1(hFile, 0);//版本Write3(hFile, 1);//标志Write4(hFile, (UINT)0);//创建时间Write4(hFile, (UINT)0);//修改时间Write4(hFile, 1);//轨道标识Write4(hFile, 0);//保留Write4(hFile, mvhd_duration_time);//轨道的时间长度BYTE tkhd_reserved[8] = { 0,0,0,0,0,0,0,0 };WriteFile(hFile, tkhd_reserved, 8, NULL, NULL);//保留Write2(hFile, 0);//视频层Write2(hFile, 0);//分组信息Write2(hFile, 0);//音量Write2(hFile, 0);//保留WriteFile(hFile, matrix, 36, NULL, NULL);//视频变换矩阵Write2(hFile, (WORD)mInit.VideoWidth); Write2(hFile, 0);//视频宽度Write2(hFile, (WORD)mInit.VideoHeight); Write2(hFile, 0);//视频高度Write4(hFile, mdia_size);//mdia box大小WriteFile(hFile, "mdia", 4, NULL, NULL);//mdia box标识Write4(hFile, mdhd_size);//mdhd box大小WriteFile(hFile, "mdhd", 4, NULL, NULL);//mdhd box标识Write1(hFile, 1);//版本Write3(hFile, 0);//标志Write8(hFile, (ULONGLONG)0); //创建时间Write8(hFile, (ULONGLONG)0); //修改时间UINT mdhd_time_scale = 1000000;//1微秒Write4(hFile, mdhd_time_scale);//时间刻度分母(分子为1)ULONGLONG mdhd_duration_time = (ULONGLONG)((double)VideoSampleDur * (double)VideoSizeAry.GetCount() / (double)10000000 * (double)mdhd_time_scale);Write8(hFile, mdhd_duration_time);//持续时间Write2(hFile, 0);//语言码Write2(hFile, 0);//预定义Write4(hFile, hdlr_size);//hdlr box大小   WriteFile(hFile, "hdlr", 4, NULL, NULL);//hdlr box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 0);//预定义WriteFile(hFile, "vide", 4, NULL, NULL);//轨道类型BYTE hdlr_reserved[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };WriteFile(hFile, hdlr_reserved, 12, NULL, NULL);//保留char Handler_ch[6] = { 'v','i','d','e','o',0 };WriteFile(hFile, Handler_ch, 6, NULL, NULL);//处理类型Write4(hFile, minf_size);//minf box大小WriteFile(hFile, "minf", 4, NULL, NULL);//minf box标识Write4(hFile, vmhd_size);//vmhd box大小WriteFile(hFile, "vmhd", 4, NULL, NULL);//vmhd box标识Write1(hFile, 0);//版本Write3(hFile, 1);//标志Write2(hFile, 0);//视频合成模式。0,不使用合成Write2(hFile, 0); Write2(hFile, 0); Write2(hFile, 0);//合成使用的颜色Write4(hFile, dinf_size);//dinf box大小WriteFile(hFile, "dinf", 4, NULL, NULL);//dinf box标识Write4(hFile, 28);//dref box大小WriteFile(hFile, "dref", 4, NULL, NULL);//dref box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数Write4(hFile, 12);//url box大小WriteFile(hFile, "url ", 4, NULL, NULL);//url box标识BYTE url_ch[4] = { 0,0,0,1 };WriteFile(hFile, url_ch, 4, NULL, NULL);//引用索引Write4(hFile, stbl_size);//stbl box大小WriteFile(hFile, "stbl", 4, NULL, NULL);//stbl box标识Write4(hFile, stsd_size);//stsd box大小    WriteFile(hFile, "stsd", 4, NULL, NULL);//stsd box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数Write4(hFile, avc1_size);//avc1 box大小     WriteFile(hFile, "avc1", 4, NULL, NULL);//avc1 box标识BYTE avc1_ch[6] = { 0,0,0,0,0,0 };WriteFile(hFile, avc1_ch, 6, NULL, NULL);//保留Write2(hFile, 1);//数据引用索引Write2(hFile, 0);//预定义Write2(hFile, 0);//保留UINT avc1_pre_defined[3] = { 0,0,0 };WriteFile(hFile, avc1_pre_defined, 12, NULL, NULL);//预定义Write2(hFile, (WORD)mInit.VideoWidth);//宽Write2(hFile, (WORD)mInit.VideoHeight);//高Write2(hFile, 72); Write2(hFile, 0);//水平分辨率Write2(hFile, 72); Write2(hFile, 0);//垂直分辨率Write4(hFile, 0);//保留Write2(hFile, 1);//单个样本中的帧数量BYTE compressor_name[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };WriteFile(hFile, compressor_name, 32, NULL, NULL);//压缩器名称Write2(hFile, 24);//位深度Write2(hFile, 65535);//预定义Write4(hFile, avcC_size);//avcC box大小WriteFile(hFile, "avcC", 4, NULL, NULL);//avcC box标识Write1(hFile, 1);//版本Write1(hFile, (BYTE)eAVEncH264VProfile_Main);//配置文件Write1(hFile, 0);//兼容文件Write1(hFile, (BYTE)255);//编码级别Write1(hFile, 0xFF);Write1(hFile, 1);//sps数量Write2(hFile, SpsSize);//sps大小WriteFile(hFile, SPS, SpsSize, NULL, NULL);//spsWrite1(hFile, 1);//pps数量Write2(hFile, PpsSize);//pps大小WriteFile(hFile, PPS, PpsSize, NULL, NULL);//ppsWrite4(hFile, stts_size);//stts box大小WriteFile(hFile, "stts", 4, NULL, NULL);//stts box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数Write4(hFile, (UINT)VideoSizeAry.GetCount());//样本数量UINT SampleDur = (UINT)((double)VideoSampleDur / (double)10000000 * (double)mdhd_time_scale);Write4(hFile, SampleDur);//单个样本的时长Write4(hFile, stss_size);//stss box大小     WriteFile(hFile, "stss", 4, NULL, NULL);//stss box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)VideoKeyFram.GetCount());//条目数for (UINT i = 0; i < (UINT)VideoKeyFram.GetCount(); i++){Write4(hFile, VideoKeyFram.GetAt(i));//写关键帧序号}Write4(hFile, stsc_size);//stsc box大小   WriteFile(hFile, "stsc", 4, NULL, NULL);//stsc box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 2);//条目数Write4(hFile, 1); Write4(hFile, 100); Write4(hFile, 1);//写条目(范围起始块序号,每个块中样本的数量,适用的stsd)Write4(hFile, co64Enter); Write4(hFile, LastEnter); Write4(hFile, 1);Write4(hFile, stsz_size);//stsz box大小    WriteFile(hFile, "stsz", 4, NULL, NULL);//stsz box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 0);//默认样本大小Write4(hFile, (UINT)VideoSizeAry.GetCount());//条目数for (UINT i = 0; i < (UINT)VideoSizeAry.GetCount(); i++){Write4(hFile, (UINT)VideoSizeAry.GetAt(i));//样本大小}Write4(hFile, co64_size);//co64 box大小WriteFile(hFile, "co64", 4, NULL, NULL);//co64 box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, co64Enter);//条目数ULONGLONG VideoOffset = VideoBaseOffset;for (UINT i = 0; i < (UINT)VideoSizeAry.GetCount(); i++){if( (i % 100)==0)Write8(hFile, VideoOffset);//样本偏移量VideoOffset += VideoSizeAry.GetAt(i);}//音频trakWrite4(hFile, audio_trak_size);//trak box大小WriteFile(hFile, "trak", 4, NULL, NULL);//trak box标识Write4(hFile, audio_tkhd_size);//tkhd box大小WriteFile(hFile, "tkhd", 4, NULL, NULL);//tkhd box标识Write1(hFile, 0);//版本Write3(hFile, 1);//标志Write4(hFile, 0);//创建时间Write4(hFile, 0);//修改时间Write4(hFile, 2);//轨道标识Write4(hFile, 0);//保留UINT tkhd_duration_time = (UINT)((double)AudioSampleDur * (double)AudioSizeAry.GetCount() / (double)10000000 * (double)mvhd_time_scale);Write4(hFile, tkhd_duration_time);//轨道的时间长度BYTE audio_tkhd_reserved[8] = { 0,0,0,0,0,0,0,0 };WriteFile(hFile, audio_tkhd_reserved, 8, NULL, NULL);//保留Write2(hFile, 0);//视频层Write2(hFile, 0);//分组信息Write2(hFile, (WORD)256);//音量Write2(hFile, 0);//保留WriteFile(hFile, matrix, 36, NULL, NULL);//视频变换矩阵Write2(hFile, 0); Write2(hFile, 0);//宽度Write2(hFile, 0); Write2(hFile, 0);//高度Write4(hFile, audio_mdia_size);//mdia box大小WriteFile(hFile, "mdia", 4, NULL, NULL);//mdia box标识Write4(hFile, audio_mdhd_size);//mdhd box大小WriteFile(hFile, "mdhd", 4, NULL, NULL);//mdhd box标识Write1(hFile, 1);//版本Write3(hFile, 0);//标志Write8(hFile, (ULONGLONG)0); //创建时间Write8(hFile, (ULONGLONG)0); //修改时间UINT mdhd_time_scaleA = 1000000;//1微秒Write4(hFile, mdhd_time_scaleA);//时间刻度分母(分子为1)ULONGLONG audio_dur = (ULONGLONG)((double)AudioSampleDur * (double)AudioSizeAry.GetCount() / (double)10000000 * (double)mdhd_time_scaleA);Write8(hFile, audio_dur);//持续时间Write2(hFile, 0);//语言码Write2(hFile, 0);//预定义Write4(hFile, audio_hdlr_size);//hdlr box大小   WriteFile(hFile, "hdlr", 4, NULL, NULL);//hdlr box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 0);//预定义WriteFile(hFile, "soun", 4, NULL, NULL);//轨道类型BYTE hdlr_reservedA[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };WriteFile(hFile, hdlr_reservedA, 12, NULL, NULL);//保留char Handler_chA[6] = { 's','o','u','n','d',0 };WriteFile(hFile, Handler_chA, 6, NULL, NULL);//处理类型Write4(hFile, audio_minf_size);//minf box大小   WriteFile(hFile, "minf", 4, NULL, NULL);//minf box标识Write4(hFile, audio_smhd_size);//smhd box大小   WriteFile(hFile, "smhd", 4, NULL, NULL);//smhd box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write2(hFile, 0);//立体声平衡Write2(hFile, 0);//保留Write4(hFile, audio_dinf_size);//dinf box大小WriteFile(hFile, "dinf", 4, NULL, NULL);//dinf box标识Write4(hFile, 28);//dref box大小WriteFile(hFile, "dref", 4, NULL, NULL);//dref box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数Write4(hFile, (UINT)12);//url box大小WriteFile(hFile, "url ", 4, NULL, NULL);//url box标识BYTE url_chA[4] = { 0,0,0,1 };WriteFile(hFile, url_chA, 4, NULL, NULL);//引用索引Write4(hFile, audio_stbl_size);//stbl box大小WriteFile(hFile, "stbl", 4, NULL, NULL);//stbl box标识Write4(hFile, audio_stsd_size);//stsd box大小=91WriteFile(hFile, "stsd", 4, NULL, NULL);//stsd box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数UINT mp4a_size = 75;Write4(hFile, mp4a_size);//mp4a box大小WriteFile(hFile, "mp4a", 4, NULL, NULL);//mp4a box标识BYTE mp4a_reserved[6] = { 0,0,0,0,0,0 };WriteFile(hFile, mp4a_reserved, 6, NULL, NULL);//保留Write2(hFile, (WORD)1);//引用索引Write8(hFile, (ULONGLONG)0);//保留Write2(hFile, 2);//声道数Write2(hFile, 16);//样本位数Write2(hFile, (WORD)0);//预定义Write2(hFile, (WORD)0);//保留Write2(hFile, (WORD)mInit.AudioSamplesPerSec); Write2(hFile, (WORD)0);//采样率UINT esds_size = 39;Write4(hFile, esds_size);//esds box大小WriteFile(hFile, "esds", 4, NULL, NULL);//esds box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志BYTE ES_DescrTag[5] = { 0x03,0x19,0,0,0 };WriteFile(hFile, &ES_DescrTag, 5, NULL, NULL);//ES_DescrTagBYTE DecConfigDescrTag[4] = { 0x04,0x11,0x40,0x15 };WriteFile(hFile, &DecConfigDescrTag, 4, NULL, NULL);//DecConfigDescrTagWrite3(hFile, 0);//缓冲区大小Write4(hFile, 0);//最大码率Write4(hFile, AvgBytes);//平均码率if (mInit.AudioSamplesPerSec == 48000)//采样率48000,声道2;AAC编码器只支持48000,44100采样率{BYTE DecSpecificInfotag[4] = { 0x05,0x02,0x11, 0x90 };WriteFile(hFile, &DecSpecificInfotag, 4, NULL, NULL);}else//采样率44100,声道2{BYTE DecSpecificInfotag[4] = { 0x05,0x02,0x12, 0x10 };WriteFile(hFile, &DecSpecificInfotag, 4, NULL, NULL);}BYTE SLConfigDescrTag[3] = { 0x06,0x01,0x02 };WriteFile(hFile, &SLConfigDescrTag, 3, NULL, NULL);//SLConfigDescrTagWrite4(hFile, audio_stts_size);//stts box大小=24WriteFile(hFile, "stts", 4, NULL, NULL);//stts box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, (UINT)1);//条目数UINT sample_dur = (UINT)((double)AudioSampleDur / (double)10000000 * (double)mdhd_time_scaleA);//单个样本的时长UINT AudioCount = AudioSizeAry.GetCount();Write4(hFile, AudioCount); Write4(hFile, sample_dur);//条目:音频样本数量;单个样本的时长Write4(hFile, audio_stsc_size);//stsc box大小WriteFile(hFile, "stsc", 4, NULL, NULL);//stsc box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 2);//条目数Write4(hFile, 1); Write4(hFile, 100); Write4(hFile, 1);//写条目(范围起始块序号,每个块中样本的数量,适用的stsd)Write4(hFile, Aco64Enter); Write4(hFile, ALastEnter); Write4(hFile, 1);Write4(hFile, audio_stsz_size);//stsz box大小WriteFile(hFile, "stsz", 4, NULL, NULL);//stsz box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, 0);//默认样本大小Write4(hFile, (UINT)AudioSizeAry.GetCount());//条目数for (UINT i = 0; i < (UINT)AudioSizeAry.GetCount(); i++){UINT size = (UINT)AudioSizeAry.GetAt(i);Write4(hFile, size);//样本大小}Write4(hFile, audio_co64_size);//co64 box大小WriteFile(hFile, "co64", 4, NULL, NULL);//co64 box标识Write1(hFile, 0);//版本Write3(hFile, 0);//标志Write4(hFile, Aco64Enter);//条目数ULONGLONG AudioOffset = AudioBaseOffset;for (UINT i = 0; i < (UINT)AudioSizeAry.GetCount(); i++){if ((i % 100) == 0)Write8(hFile, AudioOffset);//样本偏移量AudioOffset += AudioSizeAry.GetAt(i);}VideoKeyFram.RemoveAll();//清空视频关键帧序号数组VideoSizeAry.RemoveAll();//清空视频样本大小数组AudioSizeAry.RemoveAll();//清空音频样本大小数组
}DWORD WINAPI AudioWriterThread(LPVOID lp)
{Mp4Writer* pMp4Writer = (Mp4Writer*)lp;pMp4Writer->hAFile = CreateFile(L"音频样本临时文件.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建音频样本临时文件if (INVALID_HANDLE_VALUE == pMp4Writer->hAFile){MessageBox(0, L"创建临时文件失败", L"写MP4", MB_OK);pMp4Writer->hAFile = NULL;return 0;}HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础if (hr != S_OK){MessageBox(NULL, L"初始化媒体基础失败", L"写MP4", MB_OK); return 0;}IMFTransform *pAACEncoder = NULL;GUID CLSID_AACEncoderMft = { 0x93af0c51, 0x2275, 0x45d2, 0xa3, 0x5b, 0xf2, 0xba, 0x21, 0xca, 0xed, 0x00 };//AAC编码器的类标识符hr = CoCreateInstance(CLSID_AACEncoderMft, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pAACEncoder));//创建AAC音频编码器(媒体基础转换)if (hr != S_OK){MessageBox(NULL, L"AAC音频编码器创建失败", L"AAC编码器", MB_OK); return 0;}hr = pAACEncoder->QueryInterface(IID_ICodecAPI, (void**)&pMp4Writer->pAacAPI);pMp4Writer->pAQueue = new CQueue(1000004);//1M+4IMFMediaType* pInType = NULL;//输入媒体类型hr = MFCreateMediaType(&pInType);//创建空的媒体类型hr = pInType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频hr = pInType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//子类型PCMhr = pInType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32)16);//输入样本位数必须为16位hr = pInType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)pMp4Writer->mInit.AudioSamplesPerSec);//输入采样率;只允许44100,48000hr = pInType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, (UINT32)2);//两声道hr = pAACEncoder->SetInputType(NULL, pInType, 0);//设置AAC音频编码器输入媒体类型SafeRelease(&pInType);IMFMediaType *pOutType = NULL;//输出媒体类型hr = MFCreateMediaType(&pOutType);//创建空的媒体类型hr = pOutType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频hr = pOutType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);//子类型AAChr = pOutType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32)16);//输出样本位数必须为16位hr = pOutType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)pMp4Writer->mInit.AudioSamplesPerSec);//采样率;必须与输入相同hr = pOutType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, (UINT32)2);//声道数,必须与输入相同hr = pOutType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, (UINT32)12000);//传输率;只允许12000,16000,20000,24000hr = pAACEncoder->SetOutputType(NULL, pOutType, 0);//设置AAC音频编码器输出媒体类型SafeRelease(&pOutType);hr = pAACEncoder->GetOutputCurrentType(0, &pOutType);hr = pOutType->GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &pMp4Writer->AvgBytes);//获取输出传输率SafeRelease(&pOutType);BYTE* pS = new BYTE[1000000]; LONG len;ULONGLONG FrameCount = 0;//音频帧数量double Ddur = (double)10000000 / (double)pMp4Writer->mInit.AudioSamplesPerSec;//一个音频帧的持续时间,单位100纳秒SetEvent(pMp4Writer->hAudioReady);//发送“音频线程初始化完成”信号Agan:DWORD mExit = WaitForSingleObject(pMp4Writer->mInit.hExit, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{delete pMp4Writer->pAQueue; pMp4Writer->pAQueue = NULL; delete[] pS; pS = NULL;SafeRelease(&pMp4Writer->pAacAPI); SafeRelease(&pAACEncoder);MFShutdown();//关闭媒体基础return 1;}DWORD mStop = WaitForSingleObject(pMp4Writer->mInit.hStop, 0);if (mStop != WAIT_OBJECT_0)//如果“停止”无信号{BOOL BReduce = pMp4Writer->pAQueue->Reduce(pMp4Writer->mInit.hStop, pS, len);//从队列中获取样本if (BReduce)//如果读取样本成功{CreateBuffer:IMFMediaBuffer* pMFBuffer = NULL;hr = MFCreateMemoryBuffer(len, &pMFBuffer);//创建媒体基础缓冲区if (hr != S_OK || pMFBuffer == NULL)//如果创建失败{Sleep(1); goto CreateBuffer;//再次创建}BYTE* pData = NULL;hr = pMFBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区CopyMemory(pData, pS, len);//复制数据到媒体基础样本缓冲区hr = pMFBuffer->Unlock();//解锁媒体基础缓冲区hr = pMFBuffer->SetCurrentLength((DWORD)len);//设置媒体基础缓冲区的数据长度CreateSample:IMFSample* pMFSample = NULL;if (SUCCEEDED(hr)){hr = MFCreateSample(&pMFSample);//创建媒体基础样本if (hr != S_OK || pMFSample == NULL)//如果创建失败{Sleep(1); goto CreateSample;//再次创建}}hr = pMFSample->AddBuffer(pMFBuffer);//添加缓冲区到媒体基础样本LONGLONG star = (LONGLONG)((double)FrameCount * Ddur), dur = (LONGLONG)((double)(len / 4) * Ddur);FrameCount += len / 4;if (hr == S_OK){hr = pMFSample->SetSampleTime(star);//设置媒体基础样本显示时间hr = pMFSample->SetSampleDuration(dur);//设置媒体基础样本持续时间}RePut:hr = pAACEncoder->ProcessInput(NULL, pMFSample, 0);//向AAC音频编码器传递输入数据if (hr == S_OK)//如果传递输入成功{SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本goto Agan;//继续下一次传递输入}if (MF_E_NOTACCEPTING == hr)//如果不可以传递输入。MF_E_NOTACCEPTING表示已不能接收更多输入{pMp4Writer->GetAOutput(pAACEncoder);//获取编码器输出goto RePut;//传递输入失败时,需将此次的样本再次传递到输入}}}goto Agan;
}HRESULT Mp4Writer::GetAOutput(IMFTransform *pAACEncoder)//获取编码器输出
{
CreateOutBuffer:IMFMediaBuffer* pMFOutBuffer = NULL;HRESULT hr = MFCreateMemoryBuffer(1000000, &pMFOutBuffer);//创建输出媒体基础缓冲区,大小1Mif (hr != S_OK || pMFOutBuffer == NULL)//如果创建失败{Sleep(1); goto CreateOutBuffer;//再次创建}BYTE* pD = NULL;hr = pMFOutBuffer->Lock(&pD, NULL, NULL);
CreateOutSample:IMFSample* pMFOutSample = NULL;if (SUCCEEDED(hr)){hr = MFCreateSample(&pMFOutSample);//创建输出媒体基础样本if (hr != S_OK || pMFOutSample == NULL)//如果创建失败{Sleep(1); goto CreateOutSample;//再次创建}}hr = pMFOutSample->AddBuffer(pMFOutBuffer);//添加缓冲区到媒体基础样本MFT_OUTPUT_DATA_BUFFER OD;OD.dwStreamID = NULL;OD.pSample = pMFOutSample;//须为编码器指定输出样本OD.dwStatus = 0;OD.pEvents = NULL;DWORD status = 0;hr = pAACEncoder->ProcessOutput(MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, 1, &OD, &status);//获取AAC编码器输出数据。AAC编码器将输出数据,输出到刚才创建的输出样本的缓冲区内if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)//如果MFT需要更多的输入数据,此时已不可获取输出{SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样return S_OK;}if (hr == S_OK)//如果成功获得输出数据{LONGLONG s, d;hr = pMFOutSample->GetSampleTime(&s);//获取编码器输出样本开始时间hr = pMFOutSample->GetSampleDuration(&d);//获取编码器输出样本持续时间DWORD L;hr = pMFOutSample->GetTotalLength(&L);//获取AAC编码器输出样本有效数据长度AudioSizeAry.Add((UINT)L);//将样本大小添加到数组WriteFile(hAFile, pD, L, NULL, NULL);//写数据hr = pMFOutBuffer->Unlock();AudioSampleDur = d;}SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样本goto CreateOutBuffer;//再次获取输出,直到不可获取为止
}BOOL Mp4Writer::Init(MW_INIT init)
{DWORD dwV = WaitForSingleObject(hVThread, 0);if (dwV == WAIT_TIMEOUT)return FALSE;//如果线程已存在,返回DWORD dwA = WaitForSingleObject(hAThread, 0);if (dwA == WAIT_TIMEOUT)return FALSE;if (init.hExit == NULL || init.hStop == NULL){MessageBox(NULL, L"必须提供“停止”和“退出”事件句柄", L"写MP4", MB_OK); return FALSE;}if (init.AudioSamplesPerSec != 44100 && init.AudioSamplesPerSec != 48000){MessageBox(NULL, L"音频采样率必须为48000或44100", L"写MP4", MB_OK); return FALSE;}mInit = init;if (mInit.VideoWidth % 2)mInit.VideoWidth++;//视频宽高必须是偶数if (mInit.VideoHeight % 2)mInit.VideoHeight++;ResetEvent(mInit.hExit);//设置“退出”无信号SetEvent(mInit.hStop);//设置“停止”有信号ResetEvent(hVideoReady); //设置“视频线程初始化完成”无信号ResetEvent(hAudioReady);//设置“音频线程初始化完成”无信号hVThread = CreateThread(NULL, 0, VideoWriterThread, this, 0, NULL);hAThread = CreateThread(NULL, 0, AudioWriterThread, this, 0, NULL);WaitForSingleObject(hVideoReady, INFINITE);//等待“初始化完成”信号WaitForSingleObject(hAudioReady, INFINITE);return TRUE;
}

相关文章:

MFC文件-写MP4

下载本文件 本文件将创作MP4视频文件代码整合到两个文件中&#xff08;Mp4Writer.h和Mp4Writer.cpp)&#xff0c;将IYUV视频流编码为H264&#xff0c;PCM音频流编码为AAC&#xff0c;写入MP4文件。本文件仅适用于MFC程序。 使用方法 1.创建MFC项目。 2.将Mp4Writer.h和Mp4Wri…...

8.观察者模式:思考与解读

原文地址:观察者模式&#xff1a;思考与解读 更多内容请关注&#xff1a;7.深入思考与解读设计模式 引言 在开发软件时&#xff0c;系统的某些状态可能会发生变化&#xff0c;而你希望这些变化能够自动通知到依赖它们的其他模块。你是否曾经遇到过&#xff0c;系统中某个对象…...

CMake execute_process用法详解

execute_process 是 CMake 中的一个命令&#xff0c;用于在 CMake 配置阶段&#xff08;即运行 cmake 命令时&#xff09;执行外部进程。它与 add_custom_command 或 add_custom_target 不同&#xff0c;后者是在构建阶段&#xff08;如 make 或 ninja&#xff09;执行命令。ex…...

模型加载常见问题

safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge 问题代码&#xff1a; model AutoModelForVision2Seq.from_pretrained( "/data-nvme/yang/Qwen2.5-VL-32B-Instruct", trust_remote_codeTrue, torch_dtypetorc…...

PyTorch 深度学习实战(37):分布式训练(DP/DDP/Deepspeed)实战

在上一篇文章中&#xff0c;我们探讨了混合精度训练与梯度缩放技术。本文将深入介绍分布式训练的三种主流方法&#xff1a;Data Parallel (DP)、Distributed Data Parallel (DDP) 和 DeepSpeed&#xff0c;帮助您掌握大规模模型训练的关键技术。我们将使用PyTorch在CIFAR-10分类…...

微信小程序通过mqtt控制esp32

目录 1.注册巴法云 2.设备连接mqtt 3.微信小程序 备注 本文esp32用的是MicroPython固件&#xff0c;MQTT服务用的是巴法云。 本文参考巴法云官方教程&#xff1a;https://bemfa.blog.csdn.net/article/details/115282152 1.注册巴法云 注册登陆并新建一个topic&#xff…...

1.Vue3 - 创建Vue3工程

目录 一、 基于vue-cli 脚手架二、基于vite 推荐2.1 介绍2.2 创建项目2.3 文件介绍2.3.1 extensions.json2.3.2 脚手架的根目录2.3.3 主要文件 src2.3.3.1 main.js2.3.3.2 App.vue 组件2.3.3.3 conponents 2.3.4 env.d.ts2.3.5 index.html 入口文件2.3.6 package2.3.7 tsconfig…...

AI编写的“黑科技风格、自动刷新”的看板页面

以下的 index.html 、 script.js 和 styles.css 文件&#xff0c;实现一个具有黑科技风格、自动刷新的能源管理系统实时监控看板。 html页面 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name&q…...

11-DevOps-Jenkins Pipeline流水线作业

前面已经完成了&#xff0c;通过在Jenkins中创建自由风格的工程&#xff0c;在界面上的配置&#xff0c;完成了发布、构建的过程。 这种方式的缺点就是如果要在另一台机器上进行同样的配置&#xff0c;需要一项一项去填写&#xff0c;不方便迁移&#xff0c;操作比较麻烦。 解…...

23种设计模式-结构型模式之外观模式(Java版本)

Java 外观模式&#xff08;Facade Pattern&#xff09;详解 &#x1f9ed; 什么是外观模式&#xff1f; 外观模式是结构型设计模式之一&#xff0c;为子系统中的一组接口提供一个统一的高层接口&#xff0c;使得子系统更易使用。 就像是酒店前台&#xff0c;帮你处理入住、叫…...

【JavaWeb后端开发03】MySQL入门

文章目录 1. 前言1.1 引言1.2 相关概念 2. MySQL概述2.1 安装2.2 连接2.2.1 介绍2.2.2 企业使用方式(了解) 2.3 数据模型2.3.1 **关系型数据库&#xff08;RDBMS&#xff09;**2.3.2 数据模型 3. SQL语句3.1 DDL语句3.1.1 数据库操作3.1.1.1 查询数据库3.1.1.2 创建数据库3.1.1…...

Github 热点项目 Jumpserver开源堡垒机让服务器管理效率翻倍

Jumpserver今日喜提160星&#xff0c;总星飙至2.6万&#xff01;这个开源堡垒机有三大亮点&#xff1a;① 像哆啦A梦的口袋&#xff0c;支持多云服务器一站式管理&#xff1b;② 安全审计功能超硬核&#xff0c;操作记录随时可回放&#xff1b;③ 网页终端无需装插件&#xff0…...

第七届传智杯全国IT技能大赛程序设计赛道 国赛(总决赛)—— (B组)题解

1.小苯的木棍切割 【解析】首先我们先对数列排序&#xff0c;找到其中最小的数&#xff0c;那么我们就保证了对于任意一个第i1个的值都会大于第i个的值那么第i2个的值也比第i个大&#xff0c;那么我们第i1次切木棍的时候一定会当第i个的值就变为了0的&#xff0c;第i1减去的应该…...

Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例

前言 Netty是什么&#xff1f; Netty 是一个基于 Java 的 ​高性能异步事件驱动网络应用框架&#xff0c;主要用于快速开发可维护的协议服务器和客户端。它简化了网络编程的复杂性&#xff0c;特别适合构建需要处理海量并发连接、低延迟和高吞吐量的分布式系统。 1)Netty 是…...

开源身份和访问管理(IAM)解决方案:Keycloak

一、Keycloak介绍 1、什么是 Keycloak&#xff1f; Keycloak 是一个开源的身份和访问管理&#xff08;Identity and Access Management - IAM&#xff09;解决方案。它旨在为现代应用程序和服务提供安全保障&#xff0c;简化身份验证和授权过程。Keycloak 提供了集中式的用户…...

深入理解 TCP 协议 | 流量、拥塞及错误控制机制

注&#xff1a;本文为 “TCP 协议” 相关文章合辑。 原文为繁体&#xff0c;注意术语描述差异。 略作重排&#xff0c;如有内容异常&#xff0c;请看原文。 作者在不同的文章中互相引用其不同文章&#xff0c;一并汇总于此。 可从本文右侧目录直达本文主题相关的部分&#xff…...

VSCode远程图形化GDB

VSCode远程图形化GDB 摘要一、安装VSCode1、使用.exe安装包安装VSCode2、VSCode 插件安装3、VSCode建立远程连接 二、core dump找bug1、开启core文件2、永久生效的方法3、编写测试程序4、运行结果5、查看core段错误位置6、在程序中开启core dump并二者core文件大小 三、gdbserv…...

软件工程师中级考试-上午知识点总结(上)

我总结的这些都是每年的考点&#xff0c;必须要记下来的。 1. 计算机系统基础 1.1 码 符号位0表示正数&#xff0c;符号位1表示负数。补码&#xff1a;简化运算部件的设计&#xff0c;最适合进行数字加减运算。移码&#xff1a;与前几种不同&#xff0c;1表示&#xff0c;0表…...

Python+CoppeliaSim+ZMQ remote API控制机器人跳舞

这是一个使用Python和CoppeliaSim&#xff08;V-REP&#xff09;控制ASTI人型机器人进行舞蹈动作的演示项目。 项目描述 本项目展示了如何使用Python通过ZeroMQ远程API与CoppeliaSim仿真环境进行交互&#xff0c;控制ASTI人型机器人执行预定义的舞蹈动作序列。项目包含完整的机…...

基于FreeRTOS和STM32的微波炉

一、项目简介 使用STM32F103C8T6、舵机、继电器、加热片、蜂鸣器、两个按键、LCD及DHT11传感器等硬件。进一步&#xff0c;结合FreeRTOS和状态机等软件实现了一个微波炉系统&#xff1b;实现的功能包含&#xff1a;人机交互、时间及功率设置、异常情况处理及固件升级等。 二、…...

维度建模工具箱 提纲与总结

这里写自定义目录标题 基本概念事实表和维度表BI(Business Intelligence) 产品 事实表事实表的粒度事实表的种类 维度表建模技术基本原则避免用自然键作为维度表的主键&#xff0c;而要使用类似自增的整数键避免过度规范化避免变成形同事实表的维度表 SCD(Slowly Changed Dimen…...

【沉浸式求职学习day21】【常用类分享,完结!】

沉浸式求职学习 String类&#xff08;完结&#xff09; 和 equals的区别 StringBuffer日期类DateCalendar File类 String类&#xff08;完结&#xff09; 上次讲了一些创建String类实例的方法。 今天要分享的第一个点是常考的关于String的面试题 和 equals的区别 首先是&…...

国防科大清华城市空间无人机导航推理!GeoNav:赋予多模态大模型地理空间推理能力,实现语言指令导向的空中目标导航

作者&#xff1a; Haotian Xu 1 ^{1} 1, Yue Hu 1 ^{1} 1, Chen Gao 2 ^{2} 2, Zhengqiu Zhu 1 ^{1} 1, Yong Zhao 1 ^{1} 1, Yong Li 2 ^{2} 2, Quanjun Yin 1 ^{1} 1单位&#xff1a; 1 ^{1} 1国防科技大学系统工程学院&#xff0c; 2 ^{2} 2清华大学论文标题&#xff1a;Geo…...

uniapp打ios包

uniapp在windows电脑下申请证书并打包上架 前言 该开发笔记记录了在window系统下&#xff0c;在苹果开发者网站生成不同证书&#xff0c;进行uniapp打包调试和上线发布&#xff0c;对window用户友好 注&#xff1a;苹果打包涉及到两种证书&#xff1a;开发证书 和 分发证书 …...

Redis 的指令执行方式:Pipeline、事务与 Lua 脚本的对比

Pipeline 客户端将多条命令打包发送&#xff0c;服务器顺序执行并一次性返回所有结果。可以减少网络往返延迟&#xff08;RTT&#xff09;以提升吞吐量。 需要注意的是&#xff0c;Pipeline 中的命令按顺序执行&#xff0c;但中间可能被其他客户端的命令打断。 典型场景&…...

(14)VTK C++开发示例 --- 将点投影到平面上

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 计算一个点在一个平面上的投影。 vtkPlane 是 VTK&#xff08;Visualization Toolkit&#xff09;库中的一个类&…...

快速搭建 Cpolar 内网穿透(Mac 系统)

1、Cpolar快速入门教程&#xff08;官方&#xff09; 链接地址&#xff1a;Cpolar 快速入门 2、官方教程详解 本地安装homebrew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"这个是从 git 上拉取的&#x…...

【Flink SQL实战】 UTC 时区格式的 ISO 时间转东八区时间

文章目录 一、原始数据格式二、解决方案三、其他要求 在实际开发中&#xff0c;我们常常会遇到此类情况&#xff1a;数据源里的时间格式是类似 2025-04-21T09:23:16.025Z 这种带 TimeZone 标识的 ISO 8601 格式&#xff0c;而我们需要在 Flink SQL 中将其转换成北京时间显示。 …...

动态监控进程

1.介绍: top和ps命令很相似,它们都是用来显示正在执行的进程,top和ps最大的不同之处,在于top在执行中可以更新正在执行的进程. 2.基本语法&#xff1a; top [选项] 选项说明 ⭐️僵死进程&#xff1a;内存没有释放,但是进程已经停止工作了,需要及时清理 交互操作说明 应用案…...

HADOOP 3.4.1安装和搭建(尚硅谷版~)

目录 1.配置模版虚拟机 2.克隆虚拟机 3.在hadoop102安装JDK 4.完全分布式运行模式 1.配置模版虚拟机 1.安装模板虚拟机&#xff0c;IP地址192.168.10.100、主机名称hadoop100、内存2G、硬盘20G&#xff08;有需求的可以配置4G内存&#xff0c;50G硬盘&#xff09; 2.hado…...