1 | //////////////////////////////////////////////////////////// |
---|
2 | // Copyright (C) Roman Ryltsov, 2016 |
---|
3 | // Created by Roman Ryltsov roman@alax.info |
---|
4 | // |
---|
5 | // A permission to re-use this source code is granted as long as copyright notice and |
---|
6 | // reference to source website http://alax.info is retained. |
---|
7 | |
---|
8 | #include "stdafx.h" |
---|
9 | #include "libav.h" |
---|
10 | |
---|
11 | //////////////////////////////////////////////////////////// |
---|
12 | // CModule |
---|
13 | |
---|
14 | class CModule : |
---|
15 | public CAtlExeModuleT<CModule> |
---|
16 | { |
---|
17 | public: |
---|
18 | |
---|
19 | //////////////////////////////////////////////////////// |
---|
20 | // CBlob |
---|
21 | |
---|
22 | class CBlob |
---|
23 | { |
---|
24 | public: |
---|
25 | CHeapPtr<BYTE> m_pnData; |
---|
26 | SIZE_T m_nDataSize; |
---|
27 | |
---|
28 | public: |
---|
29 | // CBlob |
---|
30 | CBlob() : |
---|
31 | m_nDataSize(0) |
---|
32 | { |
---|
33 | } |
---|
34 | VOID Free() |
---|
35 | { |
---|
36 | m_pnData.Free(); |
---|
37 | m_nDataSize = 0; |
---|
38 | } |
---|
39 | VOID Append(CBlob& Value) |
---|
40 | { |
---|
41 | __D(m_pnData.Reallocate(m_nDataSize + Value.m_nDataSize), E_OUTOFMEMORY); |
---|
42 | memcpy(m_pnData + m_nDataSize, Value.m_pnData, Value.m_nDataSize); |
---|
43 | m_nDataSize += Value.m_nDataSize; |
---|
44 | } |
---|
45 | }; |
---|
46 | |
---|
47 | private: |
---|
48 | BOOL m_bAnnexB; |
---|
49 | |
---|
50 | public: |
---|
51 | // CModule |
---|
52 | CModule() |
---|
53 | { |
---|
54 | m_bAnnexB = FALSE; |
---|
55 | } |
---|
56 | HRESULT PreMessageLoop(INT nShowCommand) |
---|
57 | { |
---|
58 | _V(__super::PreMessageLoop(nShowCommand)); |
---|
59 | return S_OK; |
---|
60 | } |
---|
61 | static BOOL ReadFileData(LPCTSTR pszPath, CHeapPtr<BYTE>& pnData, SIZE_T& nDataSize) |
---|
62 | { |
---|
63 | CAtlFile File; |
---|
64 | __C(File.Create(pszPath, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); |
---|
65 | static const SIZE_T g_nDataCapacity = 1 << 20; // 1 MB |
---|
66 | __D(pnData.Allocate(g_nDataCapacity), E_OUTOFMEMORY); |
---|
67 | DWORD nReadDataSize = 0; |
---|
68 | __C(File.Read(pnData, (DWORD) g_nDataCapacity, nReadDataSize)); |
---|
69 | _A(nReadDataSize < g_nDataCapacity); |
---|
70 | nDataSize = nReadDataSize; |
---|
71 | _W(pnData.Reallocate(nDataSize)); |
---|
72 | return TRUE; |
---|
73 | } |
---|
74 | static BOOL ReadFileData(LPCTSTR pszPath, CBlob& Blob) |
---|
75 | { |
---|
76 | _A(!Blob.m_pnData); |
---|
77 | return ReadFileData(pszPath, Blob.m_pnData, Blob.m_nDataSize); |
---|
78 | } |
---|
79 | template <typename LENGTH> |
---|
80 | static SIZE_T CopyNalUnits(const BYTE* pnSourceData, SIZE_T nSourceDataSize, BYTE* pnDestinationData, SIZE_T nDestinationDataCapacity, BOOL bForceLongStartCode = FALSE) |
---|
81 | { |
---|
82 | _A(sizeof (LENGTH) == 2 || sizeof (LENGTH) == 4); |
---|
83 | BYTE* pnDestinationDataPointer = pnDestinationData; |
---|
84 | for(; nSourceDataSize; ) |
---|
85 | { |
---|
86 | const SIZE_T nUnitDataSize = (typename LENGTH::CBase) *((const LENGTH*) pnSourceData); |
---|
87 | __D(sizeof (LENGTH) + nUnitDataSize <= nSourceDataSize, HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); |
---|
88 | __D((SIZE_T) (pnDestinationDataPointer + 4 + nUnitDataSize - pnDestinationData) <= nDestinationDataCapacity, HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); |
---|
89 | pnSourceData += sizeof (LENGTH); |
---|
90 | nSourceDataSize -= sizeof (LENGTH); |
---|
91 | static const BYTE g_pnStartCode[] = { 0x00, 0x00, 0x00, 0x01 }; |
---|
92 | BOOL bLongStartCode = FALSE; |
---|
93 | if(!bForceLongStartCode) |
---|
94 | { |
---|
95 | if(nUnitDataSize) |
---|
96 | { |
---|
97 | const UINT nNalUnitType = *pnSourceData & 0x1F; |
---|
98 | if(nNalUnitType >= 6 && nNalUnitType <= 9) // SEI, SPS, PPS, Access Unit Delimiter |
---|
99 | bLongStartCode = TRUE; |
---|
100 | } |
---|
101 | } else |
---|
102 | bLongStartCode = TRUE; |
---|
103 | SIZE_T nStartCodeSize = sizeof g_pnStartCode; |
---|
104 | if(!bLongStartCode) |
---|
105 | { |
---|
106 | memcpy(pnDestinationDataPointer, g_pnStartCode + 1, sizeof g_pnStartCode - 1); |
---|
107 | nStartCodeSize--; |
---|
108 | } else |
---|
109 | memcpy(pnDestinationDataPointer, g_pnStartCode, sizeof g_pnStartCode); |
---|
110 | pnDestinationDataPointer += nStartCodeSize; |
---|
111 | memcpy(pnDestinationDataPointer, pnSourceData, nUnitDataSize); |
---|
112 | pnSourceData += nUnitDataSize; |
---|
113 | nSourceDataSize -= nUnitDataSize; |
---|
114 | pnDestinationDataPointer += nUnitDataSize; |
---|
115 | } |
---|
116 | return pnDestinationDataPointer - pnDestinationData; |
---|
117 | } |
---|
118 | static INT CompareMediaSampleFileNames(LPCTSTR pszNameA, LPCTSTR pszNameB, INT) |
---|
119 | { |
---|
120 | return _tcscmp(pszNameA, pszNameB); |
---|
121 | } |
---|
122 | VOID ProcessFrame(CAvFrame& pAvFrame) |
---|
123 | { |
---|
124 | _A(pAvFrame->pts == AV_NOPTS_VALUE); |
---|
125 | _A(pAvFrame->pkt_dts == AV_NOPTS_VALUE); |
---|
126 | CRoArrayT<CString> Array; |
---|
127 | Array.Add(AtlFormatString(_T("width %d"), pAvFrame->width)); |
---|
128 | Array.Add(AtlFormatString(_T("height %d"), pAvFrame->height)); |
---|
129 | Array.Add(AtlFormatString(_T("key_frame %d"), pAvFrame->key_frame)); |
---|
130 | Array.Add(AtlFormatString(_T("pict_type %d"), pAvFrame->pict_type)); |
---|
131 | Array.Add(AtlFormatString(_T("pkt_pts %I64d"), pAvFrame->pkt_pts)); |
---|
132 | _tprintf(_T("Frame, %s\n"), _StringHelper::Join(Array, _T(", "))); |
---|
133 | pAvFrame; |
---|
134 | } |
---|
135 | VOID ProcessMediaSample(CAvCodecContext& pAvCodecContext, CAvFrame& pAvFrame, CBlob& Blob, LONGLONG nTime) |
---|
136 | { |
---|
137 | CAvPacketT<FALSE> AvPacket(Blob.m_pnData, Blob.m_nDataSize); |
---|
138 | CBlob LocalBlob; |
---|
139 | if(m_bAnnexB) |
---|
140 | { |
---|
141 | SIZE_T nDataCapacity = 1024 + Blob.m_nDataSize + Blob.m_nDataSize / 8; |
---|
142 | __D(LocalBlob.m_pnData.Allocate(nDataCapacity), E_OUTOFMEMORY); |
---|
143 | LocalBlob.m_nDataSize = CopyNalUnits<NUINT32>(Blob.m_pnData, Blob.m_nDataSize, LocalBlob.m_pnData, nDataCapacity); |
---|
144 | _tprintf(_T("%d Annex B bytes from %d bytes\n"), LocalBlob.m_nDataSize, Blob.m_nDataSize); |
---|
145 | AvPacket.data = LocalBlob.m_pnData; |
---|
146 | AvPacket.size = (INT) LocalBlob.m_nDataSize; |
---|
147 | } |
---|
148 | AvPacket.flags = 0; //AV_PKT_FLAG_KEY; |
---|
149 | AvPacket.dts = AV_NOPTS_VALUE; |
---|
150 | AvPacket.pts = nTime; |
---|
151 | SIZE_T nDecodeDataSize; |
---|
152 | const BOOL bFrameAvailable = pAvCodecContext.DecodeVideo(pAvFrame, &AvPacket, nDecodeDataSize); |
---|
153 | _A(nDecodeDataSize == (SIZE_T) AvPacket.size); |
---|
154 | if(bFrameAvailable) |
---|
155 | ProcessFrame(pAvFrame); |
---|
156 | } |
---|
157 | VOID RunMessageLoop() |
---|
158 | { |
---|
159 | CAvLockManager::Register(); |
---|
160 | CAvCodec::Register(TRUE); |
---|
161 | CAvCodecContext pAvCodecContext; |
---|
162 | AVCodec* pAvCodec = CAvCodec::FindDecoder(AV_CODEC_ID_H264); |
---|
163 | __D(pAvCodec, E_OUTOFMEMORY); |
---|
164 | _A(pAvCodec->capabilities & CODEC_CAP_DELAY); |
---|
165 | pAvCodecContext.Allocate(pAvCodec); |
---|
166 | pAvCodecContext->codec_type = AVMEDIA_TYPE_VIDEO; |
---|
167 | pAvCodecContext->codec_id = AV_CODEC_ID_H264; |
---|
168 | #if _DEVELOPMENT |
---|
169 | pAvCodecContext->debug = FF_DEBUG_PICT_INFO | FF_DEBUG_STARTCODE; |
---|
170 | #endif // _DEVELOPMENT && defined(_DEBUG) |
---|
171 | //pAvCodecContext->time_base.num = (INT) (VideoInfoHeader2.AvgTimePerFrame / 10000i64); |
---|
172 | //pAvCodecContext->time_base.den = 2 * 1000; |
---|
173 | //pAvCodecContext->ticks_per_frame = 2; |
---|
174 | //pAvCodecContext->pkt_timebase.num = 1; |
---|
175 | //pAvCodecContext->pkt_timebase.den = 1000; |
---|
176 | CAvFrame pAvFrame; |
---|
177 | _A(!pAvFrame); |
---|
178 | pAvFrame.Allocate(); |
---|
179 | static LPCTSTR g_pszDirectory = |
---|
180 | _T("D:\\Projects\\Alax.Info\\Repository-Private\\Utilities\\DirectShow\\H.264\\tiny.mp4 NAL Units"); |
---|
181 | m_bAnnexB = TRUE; |
---|
182 | if(m_bAnnexB) |
---|
183 | _tprintf(_T("Using H.264 Annex B\n")); |
---|
184 | #pragma region Extra Data |
---|
185 | if(!m_bAnnexB) |
---|
186 | { |
---|
187 | CBlob AvcDecoderConfigurationRecordBlob; |
---|
188 | { |
---|
189 | CRoListT<CBlob> BlobList; |
---|
190 | CRoArrayT<CBlob*> SpsBlobList, PpsBlobList; |
---|
191 | for(UINT nIndex = 0; ; nIndex++) |
---|
192 | { |
---|
193 | // NOTE: Naming sample: MediaType_00 |
---|
194 | const CString sName = AtlFormatString(_T("MediaType_%02d"), nIndex); |
---|
195 | CPath sPath = Combine(g_pszDirectory, sName); |
---|
196 | if(!sPath.FileExists()) |
---|
197 | break; |
---|
198 | CBlob& Blob = BlobList.GetAt(BlobList.AddTail()); |
---|
199 | _W(ReadFileData(sPath, Blob)); |
---|
200 | _tprintf(_T("Read %d bytes from %s\n"), Blob.m_nDataSize, sName); |
---|
201 | if(Blob.m_nDataSize < 1) |
---|
202 | continue; |
---|
203 | const UINT nNalUnitType = Blob.m_pnData[0] & 0x1F; |
---|
204 | if(nNalUnitType == 7) |
---|
205 | SpsBlobList.Add(&Blob); |
---|
206 | else |
---|
207 | if(nNalUnitType == 8) |
---|
208 | PpsBlobList.Add(&Blob); |
---|
209 | //else |
---|
210 | // continue; |
---|
211 | } |
---|
212 | if(!SpsBlobList.IsEmpty() || !PpsBlobList.IsEmpty()) |
---|
213 | { |
---|
214 | // NOTE: See MPEG-4 Part 15, 5.2.4.1.1 Syntax |
---|
215 | AvcDecoderConfigurationRecordBlob.m_nDataSize = 64 << 10; // 64 KB |
---|
216 | __D(AvcDecoderConfigurationRecordBlob.m_pnData.Allocate(AvcDecoderConfigurationRecordBlob.m_nDataSize), E_OUTOFMEMORY); |
---|
217 | BYTE* A = AvcDecoderConfigurationRecordBlob.m_pnData; |
---|
218 | A[0] = 1; // configurationVersion |
---|
219 | A[1] = 100; // MPEG-4 Part 10, profile_idc, A.2.4 High profile |
---|
220 | A[2] = 0; // profile_compatibility |
---|
221 | A[3] = 40; // MPEG-4 Part 10, level_idc |
---|
222 | A[4] = 0xFC | 3; // lengthSizeMinusOne, 4 byte long lengths |
---|
223 | A += 5; |
---|
224 | BYTE* pnSequenceParameterSetCount = A++; |
---|
225 | *pnSequenceParameterSetCount = 0xE0 | SpsBlobList.GetCount(); |
---|
226 | for(auto&& pSpsBlob: SpsBlobList) |
---|
227 | { |
---|
228 | *((NUINT16*) A) = (UINT16) pSpsBlob->m_nDataSize; |
---|
229 | A += sizeof (NUINT16); |
---|
230 | memcpy(A, pSpsBlob->m_pnData, pSpsBlob->m_nDataSize); |
---|
231 | A += pSpsBlob->m_nDataSize; |
---|
232 | } |
---|
233 | BYTE* pnPictureParameterSetCount = A++; |
---|
234 | *pnPictureParameterSetCount = 0x00 | PpsBlobList.GetCount(); |
---|
235 | for(auto&& pPpsBlob: PpsBlobList) |
---|
236 | { |
---|
237 | *((NUINT16*) A) = (UINT16) pPpsBlob->m_nDataSize; |
---|
238 | A += sizeof (NUINT16); |
---|
239 | memcpy(A, pPpsBlob->m_pnData, pPpsBlob->m_nDataSize); |
---|
240 | A += pPpsBlob->m_nDataSize; |
---|
241 | } |
---|
242 | _A((SIZE_T) (A - AvcDecoderConfigurationRecordBlob.m_pnData) <= AvcDecoderConfigurationRecordBlob.m_nDataSize); |
---|
243 | AvcDecoderConfigurationRecordBlob.m_nDataSize = A - AvcDecoderConfigurationRecordBlob.m_pnData; |
---|
244 | pAvCodecContext->extradata_size = (int) AvcDecoderConfigurationRecordBlob.m_nDataSize; |
---|
245 | pAvCodecContext->extradata = (BYTE*) AvcDecoderConfigurationRecordBlob.m_pnData; |
---|
246 | } |
---|
247 | } |
---|
248 | } |
---|
249 | #pragma endregion |
---|
250 | pAvCodecContext.Open(pAvCodec); |
---|
251 | // NOTE: Naming sample: MediaSample_00006_00001250_02 |
---|
252 | CFindFiles FindFiles; |
---|
253 | CRoArrayT<CString> NameArray; |
---|
254 | for(BOOL bFound = FindFiles.FindFirstFile(g_pszDirectory, _T("MediaSample_*")); bFound; bFound = FindFiles.FindNextFile()) |
---|
255 | NameArray.Add(FindFiles.GetFindData().cFileName); |
---|
256 | NameArray.Sort<INT>(&CModule::CompareMediaSampleFileNames, 0); |
---|
257 | //for(auto&& sName: NameArray) |
---|
258 | // _tprintf(_T("Scheduled %s\n"), sName); |
---|
259 | CBlob CurrentBlob; |
---|
260 | INT nCurrentMediaSampleIndex; |
---|
261 | LONGLONG nCurrentMediaSampleTime; |
---|
262 | INT nCurrentNalUnitIndex; |
---|
263 | for(auto&& sName: NameArray) |
---|
264 | { |
---|
265 | CBlob Blob; |
---|
266 | _W(ReadFileData(Combine(g_pszDirectory, sName), Blob)); |
---|
267 | _tprintf(_T("Read %d bytes from %s\n"), Blob.m_nDataSize, sName); |
---|
268 | CRoArrayT<CString> Array; |
---|
269 | _StringHelper::Split(sName, _T('_'), Array); |
---|
270 | __D(Array.GetCount() == 4, E_UNNAMED); |
---|
271 | INT nMediaSampleIndex; |
---|
272 | LONGLONG nMediaSampleTime; |
---|
273 | INT nNalUnitIndex; |
---|
274 | _W(AtlStringToInteger(Array[1], nMediaSampleIndex)); |
---|
275 | _W(AtlStringToInteger(Array[2], nMediaSampleTime)); |
---|
276 | _W(AtlStringToInteger(Array[3], nNalUnitIndex)); |
---|
277 | _W(Blob.m_pnData.Reallocate(sizeof (NUINT32) + Blob.m_nDataSize)); |
---|
278 | memmove(Blob.m_pnData + sizeof (NUINT32), Blob.m_pnData, Blob.m_nDataSize); |
---|
279 | *((NUINT32*) (BYTE*) Blob.m_pnData) = (UINT32) Blob.m_nDataSize; |
---|
280 | Blob.m_nDataSize += sizeof (NUINT32); |
---|
281 | if(CurrentBlob.m_pnData) |
---|
282 | { |
---|
283 | if(nMediaSampleIndex != nCurrentMediaSampleIndex) |
---|
284 | { |
---|
285 | _A(nMediaSampleIndex > nCurrentMediaSampleIndex); |
---|
286 | ProcessMediaSample(pAvCodecContext, pAvFrame, CurrentBlob, nCurrentMediaSampleTime); |
---|
287 | CurrentBlob.Free(); |
---|
288 | } else |
---|
289 | { |
---|
290 | _A(nMediaSampleIndex == nCurrentMediaSampleIndex); |
---|
291 | _A(nMediaSampleTime == nCurrentMediaSampleTime); |
---|
292 | _A(nNalUnitIndex > nCurrentNalUnitIndex); |
---|
293 | } |
---|
294 | } |
---|
295 | nCurrentMediaSampleIndex = nMediaSampleIndex; |
---|
296 | nCurrentMediaSampleTime = nMediaSampleTime; |
---|
297 | nCurrentNalUnitIndex = nNalUnitIndex; |
---|
298 | CurrentBlob.Append(Blob); |
---|
299 | } |
---|
300 | if(CurrentBlob.m_pnData) |
---|
301 | { |
---|
302 | ProcessMediaSample(pAvCodecContext, pAvFrame, CurrentBlob, nCurrentMediaSampleTime); |
---|
303 | CurrentBlob.Free(); |
---|
304 | } |
---|
305 | _tprintf(_T("Draining...\n")); |
---|
306 | for(; ; ) |
---|
307 | { |
---|
308 | CAvPacketT<FALSE> AvPacket(NULL, 0); |
---|
309 | AvPacket.flags = 0; //AV_PKT_FLAG_KEY; |
---|
310 | AvPacket.dts = AV_NOPTS_VALUE; |
---|
311 | AvPacket.pts = AV_NOPTS_VALUE; |
---|
312 | DWORD nDataSize; |
---|
313 | const BOOL bFrameAvailable = pAvCodecContext.DecodeVideo(pAvFrame, &AvPacket, nDataSize); |
---|
314 | _A(!nDataSize); |
---|
315 | if(bFrameAvailable) |
---|
316 | ProcessFrame(pAvFrame); |
---|
317 | } |
---|
318 | } |
---|
319 | }; |
---|
320 | |
---|
321 | int _tmain(int argc, _TCHAR* argv[]) |
---|
322 | { |
---|
323 | CModule Module; |
---|
324 | Module.WinMain(SW_SHOWNORMAL); |
---|
325 | return 0; |
---|
326 | } |
---|
327 | |
---|