MFC文件-写MP4
下载本文件
本文件将创作MP4视频文件代码整合到两个文件中(Mp4Writer.h和Mp4Writer.cpp),将IYUV视频流,PCM音频流写入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接口,用于配置编码器。如果需要对编码器进行配置,须在初始化函数调用后,设置“停止”无信号之前。
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视频文件代码整合到两个文件中(Mp4Writer.h和Mp4Writer.cpp),将IYUV视频流,PCM音频流写入MP4文件。本文件仅适用于MFC程序。 使用方法 1.创建MFC项目。 2.将Mp4Writer.h和Mp4Writer.cpp文件复制到项目目录下。 3…...
PyTorch 深度学习实战(39):归一化技术对比(BN/LN/IN/GN)
在上一篇文章中,我们全面解析了注意力机制的发展历程。本文将深入探讨深度学习中的归一化技术,对比分析BatchNorm、LayerNorm、InstanceNorm和GroupNorm四种主流方法,并通过PyTorch实现它们在图像分类和生成任务中的应用效果。 一、归一化技术…...
C#/.NET/.NET Core技术前沿周刊 | 第 35 期(2025年4.14-4.20)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。 欢迎投稿、推荐…...
柱状图QCPBars
一、QCPBars 概述 QCPBars 是 QCustomPlot 中用于绘制柱状图/条形图的类,支持单组或多组柱状图显示,可自定义宽度、颜色和间距等属性。 二、主要属性 属性类型描述widthdouble柱子的宽度(坐标轴单位)widthTypeWidthType宽度计算…...
2025-04-20 李沐深度学习4 —— 自动求导
文章目录 1 导数拓展1.1 标量导数1.2 梯度:向量的导数1.3 扩展到矩阵1.4 链式法则 2 自动求导2.1 计算图2.2 正向模式2.3 反向模式 3 实战:自动求导3.1 简单示例3.2 非标量的反向传播3.3 分离计算3.4 Python 控制流 硬件配置: Windows 11Inte…...
Nginx在微服务架构项目(Spring Cloud)中的强大作用
文章目录 一、Nginx是什么?二、Nginx在微服务架构(Spring Cloud)项目中的作用1.前端静态资源托管2.反向代理后端 API3.负载均衡4.SSL 证书与 HTTPS 支持5.缓存与压缩优化6.安全防护7.灰度发布与流量控制8.跨域处理(CORS࿰…...
Mysql相关知识2:Mysql隔离级别、MVCC、锁
文章目录 MySQL的隔离级别可重复读的实现原理Mysql锁按锁的粒度分类按锁的使用方式分类按锁的状态分类 MySQL的隔离级别 在 MySQL 中,隔离级别定义了事务之间相互隔离的程度,用于控制一个事务对数据的修改在何时以及如何被其他事务可见。MySQL 支持四种…...
解决IDEA创建SpringBoot项目没有Java版本8
问题:idea2023版本创建springboot的过程中,选择java版本时发现没有java8版本,只有java17和java20 原因:spring2.X版本在2023年11月24日停止维护了,因此创建spring项目时不再有2.X版本的选项,只能从3.1.X版本…...
第十章:Agent 的评估、调试与可观测性:确保可靠与高效
引言 随着我们一步步构建出越来越复杂的 AI Agent,赋予它们高级工具和更智能的策略,一个至关重要的问题浮出水面:我们如何知道这些 Agent 是否真的有效、可靠?当它们行为不符合预期时,我们又该如何诊断和修复问题&…...
8节串联锂离子电池组可重构buck-boost均衡拓扑结构 simulink模型仿真
8节串联锂离子电池组 极具创新性 动态分组均衡策略,支持3种均衡模式 1.最高SOC电池给最低SOC电池均衡 2.高能电池组电池给最低SOC电池均衡 3.高能电池组电池给低能电池组电池均衡 支持手动设置均衡开启阈值和终止阈值 均衡效果非常好...
Oracle EBS COGS Recognition重复生成(一借一贷)
背景 月结用户反馈“发出商品”(实际为递延销货成本)不平,本月都是正常操作月结程序,如正常操作步骤如下: 记录订单管理事务处理 (Record Order Management Transactions)收集收入确认信息 (Collect Revenue Recognition Information)生成销货成本确认事件 (Generate COGS …...
Linux命令--将控制台的输入写入文件
原文网址:Linux命令--将控制台的输入写入文件-CSDN博客 简介 本文介绍Linux将控制台的输入写入文件的方法。 方案1:cat > file1(推荐) 普通用法 cat > file1 输入结束后,用CtrlD退出。 示例 使用root权限…...
使用BQ76PL455和STM32的SAE电动方程式电动汽车智能BMS
BMS对任何电动汽车来说都是必不可少的,它可以监控电池的行为,确保安全行驶。 该项目旨在降低成本,同时为每个电池模块提供可扩展的BMS。BQ76PL455具有监测6-16个单元的能力,8通道辅助输入(用于温度监测)和多达15个其他ic用于Daisy…...
OpenCV 模板与多个对象匹配方法详解(继OpenCV 模板匹配方法详解)
文章目录 前言1.导入库2.图片预处理3.输出模板图片的宽和高4.模板匹配5.获取匹配结果中所有符合阈值的点的坐标5.1 threshold 0.9:5.2 loc np.where(res > threshold): 6.遍历所有匹配点6.1 loc 的结构回顾6.2 loc[::-1] 的作用6.2.1 为什么需要反转…...
7.0/Q1,Charls最新文章解读
文章题目:Anti-hypertensive medication adherence, socioeconomic status, and cognitive aging in the Chinese community-dwelling middle-aged and older adults ≥ 45 years: a population-based longitudinal study DOI:10.1186/s12916-025-03949-…...
【第三十二周】CLIP 论文阅读笔记
CLIP 摘要Abstract文章信息引言方法预训练推理Q&A 关键代码实验结果总结 摘要 本篇博客介绍了CLIP(Contrastive Language-Image Pre-training),这是OpenAI于2021年提出的多模态预训练模型,其核心思想是通过对比学习将图像与文…...
在 Ubuntu 系统上安装 PostgreSQL
在 Ubuntu 系统上安装 PostgreSQL 的完整指南: 一、安装 PostgreSQL(最新版本) 1. 更新软件包列表: bash sudo apt update 2. 安装 PostgreSQL 和客户端工具: bash sudo apt install postgresql po…...
【MySQL】数据类型
🏠个人主页:Yui_ 🍑操作环境:Centos7 🚀所属专栏:MySQL 文章目录 前言1. bit类型2.tinyint类型3. float类型4. decimal5. char类型6. varchar5&6 char和varchar的比较7.日期和时间类型8.enum和set总结 …...
Mac上Cursor无法安装插件解决方法
可能是微软的vscode被cursor这些新晋的AI-IDE白嫖够了,所以现在被制裁了,cursor下载不了vscode插件了。需要自己修改扩展商店源。 近期微软调整了 API 鉴权策略或限制了非官方客户端的访问权限。 解决方案 一、找到 product.json 文件 打开终端&…...
PI0 Openpi 部署(仅测试虚拟环境)
https://github.com/Physical-Intelligence/openpi/tree/main 我使用4070tisuper, 14900k,完全使用官方默认设置,没有出现其他问题。 目前只对examples/aloha_sim进行测试,使用docker进行部署, 默认使用pi0_aloha_sim模型(但是文档上没找到对应的&…...
NumPy数组和二维列表的区别
在 Python 中,NumPy 数组和二维列表在性能方面存在诸多不同,下面从存储方式、内存占用、操作速度、缓存局部性这几个角度详细分析。 存储方式 二维列表:它是 Python 内置的数据结构,列表中的每个元素实际上是一个引用࿰…...
学习设计模式《四》——单例模式
一、基础概念 单例模式的本质【控制实例数目】; 单例模式的定义:是用来保证这个类在运行期间只会被创建一个类实例;单例模式还提供了一个全局唯一访问这个类实例的访问点(即GetInstance方法)单例模式只关心类实例的创建…...
构建具备推理与反思能力的高级 Prompt:LLM 智能代理设计指南
在构建强大的 AI 系统,尤其是基于大语言模型(LLM)的智能代理(Agent)时,Prompt 设计的质量决定了系统的智能程度。传统 Prompt 通常是简单的问答或填空式指令,而高级任务需要更具结构性、策略性和…...
NLP 梳理03 — 停用词删除和规范化
一、说明 前文我们介绍了标点符号删除、文本的大小写统一,本文介绍英文文章的另一些删除内容,停用词删除。还有规范化处理。 二、什么是停用词,为什么删除它们? 2.1 停用词的定义 停用词是语言中的常用词,通常语义…...
算法—插入排序—js(小数据或基本有序数据)
插入排序原理:(适合小规模数据) 将数组分为“已排序”和“未排序”两部分,逐个将未排序元素插入到已排序部分的正确位置。 特点: 时间复杂度:平均 O(n),最优(已有序)O(n…...
家庭电脑隐身后台自动截屏软件,可远程查看
7-4 本文介绍一个小软件,可以在电脑后台运行,并且记录电脑的屏幕画面保存下来,并且可以远程提取查看。 可以用于记录长时间运行的软件的执行画面过程,或者用于记录家庭中小孩使用电脑的过程,如果没有好好上网课&…...
【Agent】AI智能体评测基座AgentCLUE-General
note AgentCLUE-General将题目划分为“联网检索”、“数据分析”、“多模态理解”和“多场景组合”任务AgentCLUE-General为每个题目都提供一个标准答案,将Agent智能体的答案与标准答案进行规则匹配判断对错 文章目录 note一、任务划分和场景划分二、答案提取的pro…...
最新iOS性能测试方法与教程
一、工具instrument介绍 使用Xcode的instrument进行测试,instrument自带了很多性能方面的测试工具,如图所示: 二、常见性能测试内容 不管是安卓还是iOS的性能测试,常见的性能测试都要包含这五个方面: 1、内存ÿ…...
多模态大语言模型arxiv论文略读(三十)
Mastering Text-to-Image Diffusion: Recaptioning, Planning, and Generating with Multimodal LLMs ➡️ 论文标题:Mastering Text-to-Image Diffusion: Recaptioning, Planning, and Generating with Multimodal LLMs ➡️ 论文作者:Ling Yang, Zhao…...
【AI论文】CLIMB:基于聚类的迭代数据混合自举语言模型预训练
摘要:预训练数据集通常是从网络内容中收集的,缺乏固有的领域划分。 例如,像 Common Crawl 这样广泛使用的数据集并不包含明确的领域标签,而手动整理标记数据集(如 The Pile)则是一项劳动密集型工作。 因此&…...
AI大模型发展现状与MCP协议诞生的技术演进
1. 大模型能力边界与用户痛点(2023年) 代表模型:GPT-4(OpenAI)、Claude 3(Anthropic)、通义千问(阿里云)等展现出强大的生成能力,但存在明显局限:…...
从malloc到free:动态内存管理全解析
1.为什么要有动态内存管理 我们已经掌握的内存开辟方法有: int main() {int val 20;//在栈空间上开辟四个字节char arr[20] { 0 };//在栈空间上开辟10个字节的连续空间return 0; }上述开辟的内存空间有两个特点: 1.空间开辟的时候大小已经固定 2.数组…...
CSS值和单位
CSS值和单位 CSS 中的值和单位是构建样式的基础,它们定义了属性的具体表现方式。值用于定义样式属性的具体取值,而单位用于指定这些值的度量方式。CSS中常用的值和单位如下: 1.长度单位 px : 像素,绝对单位 em : 相对于元素的字…...
Redis高级篇之I/O多路复用的引入解析
文章目录 一、问题背景1. 高并发连接的管理2. 避免阻塞和延迟3. 减少上下文切换开销4. 高效的事件通知机制5. 简化编程模型6. 低延迟响应本章小节 二、I/O多路复用高性能的本质1. 避免无意义的轮询:O(1) 事件检测2. 非阻塞 I/O 零拷贝:最大化 CPU 利用率…...
FTP协议命令和响应码
文章目录 📦 一、什么是 FTP 协议?🧾 二、FTP 常见命令(客户端发送)📡 三、FTP 响应码(服务端返回)📌 响应码分类(第一位)✅ 常见成功响应码&…...
在win上安装Ubuntu安装Anaconda(linx环境)
一,安装Ubuntu 1. 在 Microsoft 商城去下载Ubuntu(LTS:是长期维护的版本) 2.安装完之后启动程序,再重新打开一个黑窗口: wsl --list --verbose 3.关闭Ubuntu wsl --shutdown Ubuntu-22.04 WSL2 Ubuntu-20.04文件太占c盘空间,…...
【Elasticsearch入门到落地】11、RestClient初始化索引库
接上篇《10、初始化RestClient》 上一篇我们已经完成了RestHighLevelClient的初始化工作,本篇将正式进入索引库的创建阶段。我们将使用Java代码来创建酒店数据的索引库。 一、准备工作 1. 创建常量类 首先,我们需要定义一个常量类来存放索引库的mappi…...
远程服务调用的一些注意事项
引言 最近工作中,遇到了一些关于远程服务调用的问题,背景是调用三方接口获取某些特征数据,但由于调用出现了超时,导致业务本身的接口的可用行降低。因此整理一些远程服务调用时的注意事项,通过不同维度的考虑来提高系…...
QML 样式库
在 QML 中,样式库(或 UI 框架)用于快速构建一致且美观的界面。Qt/QML 本身不提供内置的完整样式库,但可以通过以下方式实现样式管理或使用第三方库。 1. Qt Quick Controls 2 样式系统 Qt Quick Controls 2 是官方提供的 UI 组件…...
[RHEL8] 指定rpm软件包的更高版本模块流
背景:挂载RHEL ISO使用kickstart安装操作系统,安装包未指定安装perl,但是安装完可以查到其版本,且安装的是ISO中多个版本中的最低版本。 原因:(1)为什么没有装perl,perl -v可以看到版…...
使用Python可视化洛伦兹变换
引言 大家好!今天我们将探讨一个非常有趣且重要的物理概念—洛伦兹变换。它是相对论的核心内容之一,描述了在高速运动下,时间、长度以及其他物理量是如何发生变化的。通过使用 Python 进行可视化,我们不仅可以更好地理解这个概念,还能感受到物理世界中的奇妙之处。 什么…...
【二叉树专题】一道深入浅出的 DFS 题:求二叉树的直径(含通俗易懂讲解)
题目: 给你一棵二叉树的根节点,返回这棵树的 直径。 直径 是任意两个节点路径中,最长的一条路径所经过的边数。 比如下面这棵树: 1/ \2 3/ \ 4 5它的最长路径是:4 → 2 → 5 或者 4 → 2 → 1 → 3,…...
考研系列-计算机网络-第三章、数据链路层
一、数据链路层的功能 1.知识点总结 2.习题总结...
医药采购系统平台第10天02:按药品分类的统计按供货商统计按医院统计统计数据的导出DWR的配置和应用
如果想要获取相关的源码,笔记,和相关工具,对项目需求的二次开发,可以关注我并私信!!! 一 按药品分类的统计实现 1 按药品分类统计的需求 按药品统计:在指定时间段中采购量、采购金…...
Navicat、DataGrip、DBeaver在渲染 BOOLEAN 类型字段时的一种特殊“视觉风格”
文章目录 前言✅ 为什么 Boolean 字段显示为 [ ]?✅ 如何验证实际数据类型?✅ 小结 前言 看到的 deleted: [ ] 并不是 Prisma 的问题,而是数据库客户端(如 Navicat、DataGrip、DBeaver)在渲染 BOOLEAN 类型字段时的一种…...
(undone) 吴恩达版提示词工程 2. 指南
url: https://www.bilibili.com/video/BV1Z14y1Z7LJ?spm_id_from333.788.videopod.episodes&vd_source7a1a0bc74158c6993c7355c5490fc600&p2 别人的笔记 url: https://zhuanlan.zhihu.com/p/626966526 指导原则(Guidelines) 编写提示词有两个…...
VLC搭建本机的rtsp直播推流和拉流
媒体---流---捕获设备,选择摄像头,点击串流 x下一步 选择rtsp,点击添加 看到了端口,并设置路径: 选择Video -H 264 mp3(TS) 点击下一个, 点击流,就开始推流了 拉流,观看端&#x…...
Rocky Linux 9.1 修改网卡和DNS
在 Rocky Linux 9.1 中修改网卡和 DNS 配置可以通过 NetworkManager 工具实现(推荐)或直接编辑配置文件。以下是两种方法的详细步骤: 方法一:使用 nmcli 命令行工具(动态生效) 查看当前网络连接nmcli connection show # 输出示例: # NAME UUID …...
Web前端:常用的布局属性
常见的布局方式有哪些? float:浮动布局 position 定位布局 flex 弹性布局(display) table 表格布局(弃用) 一、HTML5 语义化布局标签 这些标签本身不提供布局能力,但能增强页面结构…...
XSS学习2
一、客户端的Cookie 1. 无状态的影响 无状态问题: HTTP协议的无状态特性导致每次请求都是独立的,无法保持会话。例如,在银行办理业务时,柜员不需要重复询问客户信息,但在计算机网络中,每次HTTP请求都需要重新认证用户…...