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;
}