下载本文件
本文件将创作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_RELEASE
template <class T> void SafeRelease(T** ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
#endif //SAFE_RELEASE
struct 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 box
HRESULT 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,最大计数10
hReduce = 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);//“已用”信号量加1
return 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);//“可用”信号量加1
return 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);//设置子类型H264
if (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 box
CloseHandle(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号
//视频trak
Write4(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);//sps
Write1(hFile, 1);//pps数量
Write2(hFile, PpsSize);//pps大小
WriteFile(hFile, PPS, PpsSize, NULL, NULL);//pps
Write4(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);
}
//音频trak
Write4(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大小=91
WriteFile(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_DescrTag
BYTE DecConfigDescrTag[4] = { 0x04,0x11,0x40,0x15 };
WriteFile(hFile, &DecConfigDescrTag, 4, NULL, NULL);//DecConfigDescrTag
Write3(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);//SLConfigDescrTag
Write4(hFile, audio_stts_size);//stts box大小=24
WriteFile(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+4
IMFMediaType* pInType = NULL;//输入媒体类型
hr = MFCreateMediaType(&pInType);//创建空的媒体类型
hr = pInType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频
hr = pInType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//子类型PCM
hr = 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,48000
hr = 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);//子类型AAC
hr = 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,24000
hr = 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);//创建输出媒体基础缓冲区,大小1M
if (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;
}