source: trunk/Utilities/FFmpeg/DecodeMpeg4Video02/Application.cpp @ 594

Last change on this file since 594 was 594, checked in by roman, 7 years ago
File size: 11.6 KB
Line 
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
14class CModule :
15        public CAtlExeModuleT<CModule>
16{
17public:
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
47private:
48        BOOL m_bAnnexB;
49
50public:
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_PTR 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 | (BYTE) 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 | (BYTE) 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                        SIZE_T nDataSize;
313                        const BOOL bFrameAvailable = pAvCodecContext.DecodeVideo(pAvFrame, &AvPacket, nDataSize);
314                        _A(!nDataSize);
315                        if(!bFrameAvailable)
316                                break;
317                        ProcessFrame(pAvFrame);
318                }
319        }
320};
321
322int _tmain(int argc, _TCHAR* argv[])
323{
324        CModule Module;
325        Module.WinMain(SW_SHOWNORMAL);
326        return 0;
327}
328
Note: See TracBrowser for help on using the repository browser.