H.264 Tutorial YT1-1627-000
Transcript of H.264 Tutorial YT1-1627-000
NETWORK CAMERA Protocol Spec.
H.264 Data Protocol Tutorial
Ver. 1.0
YT1-1627-000
April 20, 2011
CANON INC.
2
Change Tracking List
Version Date Revised page Note
Ver. 1.0 April 20, 2011 - -
3
Table of Contents
1 Sample Program for Saving H.264 Video Data .......................................................................... 4
1.1 Creating a Project .................................................................................................... 6
1.2 Creating a Camera Communication Class .............................................................. 8
1.3 Creating a NAL Data Extraction Class................................................................... 14
1.4 Creating a File Save Class .................................................................................... 26
1.5 Combining the Classes .......................................................................................... 29
4
1 Sample Program for Saving H.264 Video Data The following shows the sample program for saving H.264 video data using VC++.
This sample program acquires H.264 video data in an MP4 fragment format from the
camera, removes the MP4 header from the data, and saves it to a PC as data reproducible
with general decoders.
WvCameraSession Class
It acquires H.264 video data in the MP4 fragment format from the camera. Then, it
writes the acquired data into an MP4 buffer for passing the data to the
WvH264DataExtract class.
WvH264DataExtract Class
It extracts the H.264 video data from the MP4 buffer and removes the MP4 header.
Then, it writes the data without the MP4 header into a H.264 buffer for passing the
data to the WvFileSave class.
WvFileSave Class
It extracts the data without the MP4 header from the H.264 buffer, and then saves it to
a PC.
5
The specification for the sample program is as follows:
・ Win32 command-line application (using MFC)
・ Execution method
“SimpleH264DataExtract.exe -o {File to save to} -d {Image transmission duration (sec.)}”
When a file to save to is not specified, the save location is “C:\h264.dat”.
When the duration is not specified, 0 is specified, and the processing continues until the
program ends.
・ The settings of H.264 video data follow the present settings of the camera.
・ When the press of the Ctrl and C buttons or the end of command prompt is detected,
the end processing is performed.
In a sample code, the camera’s temporary IP address is “192.168.100.1”. For actual use,
replace the temporary IP address with an actual one.
The image data acquired from the camera is referred to as “MP4 data” and the data with the
MP4 header removed from the image data as “H.264 data”.
The sample program is written in Visual C++, and the operation has been confirmed in
Visual C++ 2008 Professional Edition.
6
1.1 Creating a Project
Select “File” > “New” > “Project”, and then select “Win32 Console Application”. Enter
“SimpleH264DataExtract” in the Name field, and then press the OK button.
7
Check the “MFC” checkbox under “Add common header files for” in “Application Settings” of
the Win32 Application Wizard, and then press the Finish button.
8
1.2 Creating a Camera Communication Class
Select “Project” > “Add Class” to open the “Add Class” dialog. Select “C++ Class”, and then
press the Add button.
9
Enter “WvCameraSession” in the Class Name field, and then press the Finish button.
The following explains what this class performs.
・ The class generates a thread in which the following is performed: executing the
video.cgi command (sessionless), acquiring H.264 video data in MP4 fragment format
from the camera and then writing the data into the MP4 data buffer.
・ The thread starts with the StartGetVideo function and stops with the StopGetVideo
function.
The main processing for the WvCameraSession class is the GetVideo function. The
following explains the flow of the function (WvCameraSession.cpp).
1. It starts an HTTP communication with the camera using WinInet.
It transmits the video.cgi command to the camera, and ends the processing when the
HTTP status code is a value other than “200”.
(Line number 32 - 76)
10
2. It acquires a response from the camera, and writes the response into the MP4 data
buffer.
It continues the processing until the stop flag becomes true due to calling the
StopGetVideo function or until the image transmission from the camera ends. When a
failure in the communication with the camera server such as network disconnection
occurs, it displays a message and ends the processing.
(Line number 78 - 106)
3. It deletes each object, and performs the end processing of the HTTP communication.
(Line number 109 - 113)
The details of the file are as follows:
WvCameraSession.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma once
#include <deque>
#include <afxmt.h>
class WvCameraSession
{
public:
WvCameraSession(CString hostname, int duration);
~WvCameraSession(void);
private:
CString hostname; //Host name of the camera
//Duration in which an image is transmitted from the camera(sec.)
int duration;
bool stop; //Flag for stopping the image data
std::deque<BYTE> * mp4Buffer; //MP4 data buffer
//Object for the exclusive control of the MP4 data buffer
CCriticalSection * keyMP4;
CWinThread * thread; //Object for the thread control
private:
static UINT ThreadProc(LPVOID pParam);
UINT GetVideo();
public:
void StartGetVideo(std::deque<BYTE> * mp4Buffer, CCriticalSection * keyMP4);
void StopGetVideo();
void WaitThreadProc();
};
11
WvCameraSession.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "StdAfx.h"
#include "WvCameraSession.h"
#include <afxinet.h>
#include <afxwin.h>
WvCameraSession::WvCameraSession(CString hostname, int duration)
{
this->stop = false;
this->mp4Buffer = NULL;
this->keyMP4 = NULL;
this->thread = NULL;
this->hostname = hostname;
this->duration = duration;
}
WvCameraSession::~WvCameraSession(void)
{
this->mp4Buffer = NULL;
this->keyMP4 = NULL;
this->thread = NULL;
}
//Acquire H.264 video data from the camera
UINT WvCameraSession::GetVideo()
{
DWORD dwStatus;
BYTE buffer[1024];
UINT readByte;
CString command;
UINT ret = 0;
command.Format(_T("%s%d"), _T("/-wvhttp-01-/video.cgi?v=h264&duration="),
duration);
CInternetSession inet = CInternetSession(_T("SimpleH264DataExtract"));
CHttpConnection *pHttpCon = inet.GetHttpConnection(hostname,
CHttpConnection::HTTP_VERB_GET,
80,
NULL,
NULL);
CHttpFile *pFile = pHttpCon->OpenRequest(CHttpConnection::HTTP_VERB_GET,
command,
NULL,
1,
NULL,
_T("HTTP/1.1"),
NULL);
try
{
pFile->SendRequest();
}
12
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
catch (CInternetException * e)
{
CString err;
TCHAR buf[512];
e->GetErrorMessage(buf, 512);
err.Format(_T("Error occurred : %d / %s\n"), e->m_dwError, buf);
::AfxMessageBox(err, MB_OK | MB_ICONEXCLAMATION);
ret = -1;
goto EXIT;
}
pFile->QueryInfoStatusCode(dwStatus);
if (dwStatus != 200)
{
CString err;
err.Format(_T("HTTP error : %d \n"), dwStatus);
::AfxMessageBox(err, MB_OK | MB_ICONEXCLAMATION);
ret = -1;
goto EXIT;
}
while (1)
{
try
{
readByte = pFile->Read(buffer, 1024);
keyMP4->Lock();
mp4Buffer->insert(mp4Buffer->end(), buffer, buffer + 1024);
keyMP4->Unlock();
if ((readByte < 1024) || (stop == true))
{
break;
}
}
catch (CInternetException * e)
{
CString err;
TCHAR buf[512];
e->GetErrorMessage(buf, 512);
err.Format(_T("Error occurred : %d / %s\n"), e->m_dwError, buf);
::AfxMessageBox(err, MB_OK | MB_ICONEXCLAMATION);
ret = -1;
goto EXIT;
}
::Sleep(1);
}
13
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
EXIT:
pFile->Close();
pHttpCon->Close();
inet.Close();
delete pFile;
delete pHttpCon;
return ret;
}
//Start the thread
UINT WvCameraSession::ThreadProc(LPVOID pParam)
{
WvCameraSession * session = (WvCameraSession*)pParam;
return session->GetVideo();
}
//Start the H.264 video data acquition processing
void WvCameraSession::StartGetVideo(std::deque<BYTE> * mp4Buffer, CCriticalSection *
keyMP4)
{
this->stop = false;
this->mp4Buffer = mp4Buffer;
this->keyMP4 = keyMP4;
thread = AfxBeginThread(ThreadProc, this);
}
//End the H.264 video data acquition processing
void WvCameraSession::StopGetVideo()
{
stop = true;
}
void WvCameraSession::WaitThreadProc()
{
WaitForSingleObject(thread->m_hThread, INFINITE);
}
14
1.3 Creating a NAL Data Extraction Class
Select “Project” > “Add Class” to open the “Add Class” dialog. Select “C++ Class”, and then
press the Add button.
Enter “WvH264DataExtract” in the Class Name field, and then press the Finish button.
The H.264 data transmitted from the camera is in the MP4 fragment format compliant with
“ISO/IEC 14496-10”, “ISO/IEC 14496-12” and “ISO/IEC 14496-15”. The data have the
following box structure.
・ ftyp (File Type Box)
It is a description about a file type. Only one box of this kind is included in the head of a
stream from the camera.
・ moov (Movie Box)
It includes initial settings items required for video playback such as image size, frame
rate and time information. Only one box of this kind is included in a stream from the
camera.
・ moof (Movie Fragment Box)
It includes a sequence No., frame type and others.
・ mdat (Media Data Box)
Main body of media data
The H.264 video data is divided in processing units called NAL (Network Abstraction Layer)
units, and encrypted. The NAL units include the following information.
・ SPS (Sequence Parameter Set) header
Information required for decoding a stream
・ PPS (Picture Parameter Set) header
Information required for decoding each picture
・ Picture data
H.264 image data (hereafter, referred to as NAL data)
15
The SPS/PPS headers are stored in the avcC (AVC Configuration) box inside the moov box.
The NAL data is stored in the mdat box.
The number of NAL data in the mdat box differs depending on the settings of the camera.
The default value is 1.
The following explains what this class performs.
・ The class generates a thread in which the following is performed: analyzing the H.264
video data acquired in the camera communication class, extracting the NAL data, and
then writing the extracted NAL data into the H.264 video data buffer.
・ The thread starts with the StartExtractData function and stops with the StopExtractData
function.
The main processing for the WvH264DataExtract class is the ExtractData function. The
following explains the flow of the function (WvH264DataExtract.cpp).
1. It extracts 1024-byte data from the H.264 data buffer where the H.264 video data in the
MP4 fragment format is stored.
(Line number 255 - 268)
2. It searches the avcC box for the SPS and PPS headers only once after the processing
starts. When it finds the headers, it writes the header information into the H.264 data
buffer. Otherwise, it ends the processing.
(Line number 270 - 289)
3. It searches the mdat box. When it does not find the mdat box, it returns to the
processing 1.
(Line number 291 - 310)
16
4. It extracts the data as much as the size of the mdat box from the MP4 data buffer.
(Line number 312 - 342)
5. It writes into the H.264 data buffer the NAL structure data taken out of the extracted
data.
(Line number 344 - 348)
6. It returns to the processing 1.
The processing continues until the stop flag becomes true. When the camera
communication class thread ends, the stop flag becomes true.
17
The details of the file are as follows:
WvH264DataExtract.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma once
#include <deque>
#include <afxmt.h>
class WvH264DataExtract
{
public:
WvH264DataExtract(void);
~WvH264DataExtract(void);
private:
//Flag for stopping the MP4 header analysis processing
bool stop;
std::deque<BYTE> * mp4Buffer; //MP4 data buffer
std::deque<BYTE> * h264Buffer; //H.264 data buffer
//Object for the exclusive control of the MP4 data buffer
CCriticalSection * keyMP4;
//Object for the exclusive control of the H.264 data buffer
CCriticalSection * keyH264;
CWinThread * thread; //Object for the thread control
private:
static UINT ThreadProc(LPVOID pParam);
UINT ExtractData();
int SPSPPS_extract(BYTE * buf, int index);
int NAL_extract(BYTE * buf, int index, int mdat_size);
void WriteBuffer(BYTE buf);
void WriteBuffers(BYTE * buf, int size);
int GetBuffers(BYTE * buf, int size);
public:
void StartExtractData(std::deque<BYTE> * mp4Buffer, std::deque<BYTE> * h264Buffer,
CCriticalSection * keyMP4, CCriticalSection * keyH264);
void StopExtractData();
void WaitThreadProc();
};
18
WvH264DataExtract.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "StdAfx.h"
#include "WvH264DataExtract.h"
#define SEARCH_SPSPPS 0
#define WRITE_SPS 1
#define WRITE_PPS 2
const BYTE hex_avcC[4] = {0x61, 0x76, 0x63, 0x43};
const BYTE hex_mdat[4] = {0x6D, 0x64, 0x61, 0x74};
const BYTE hex_SCP[4] = {0x00, 0x00, 0x00, 0x01};
WvH264DataExtract::WvH264DataExtract(void)
{
this->stop = false;
this->mp4Buffer = NULL;
this->h264Buffer = NULL;
this->keyMP4 = NULL;
this->keyH264 = NULL;
this->thread = NULL;
}
WvH264DataExtract::~WvH264DataExtract(void)
{
this->mp4Buffer = NULL;
this->h264Buffer = NULL;
this->keyMP4 = NULL;
this->keyH264 = NULL;
this->thread = NULL;
}
static size_t hex2dec(char * hex)
{
size_t num = 0;
int buf;
int i;
for (i = 0; ; i++) {
if (hex[i] >= '0' && hex[i] <= '9')
{
buf = hex[i] - '0';
}
else if (hex[i] >= 'a' && hex[i] <= 'f') {
buf = hex[i] - 'a' + 10;
}
else if (hex[i] >= 'A' && hex[i] <= 'F') {
buf = hex[i] - 'A' + 10;
}
else if (hex[i] == '\0')
{
break;
}
else
{
19
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
num = 0xFFFFFFFF;
break;
}
num *= 16;
num += buf;
if (i >= 8)
{
//Overflow
num = 0xFFFFFFFF;
break;
}
}
return(num);
}
static int GetSize(BYTE * buf, int size)
{
int i;
int data_size = 0;
BYTE size_hex_buf[4];
char size_hex[10];
char format[20] = "";
if (size > 4)
{
return 0;
}
for (i = 0; i < size; i++)
{
size_hex_buf[i] = buf[i];
strcat(format, "%02x");
}
sprintf(size_hex, format, size_hex_buf[0], size_hex_buf[1],
size_hex_buf[2], size_hex_buf[3]);
data_size = hex2dec(size_hex);
return data_size;
}
//Write the 1-byte data into the buffer
void WvH264DataExtract::WriteBuffer(BYTE buf)
{
this->keyH264->Lock();
this->h264Buffer->push_back(buf);
this->keyH264->Unlock();
}
///Write the data as much as the specified size into the buffer
void WvH264DataExtract::WriteBuffers(BYTE * buf, int size)
{
if (size < 0)
{
20
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
return;
}
this->keyH264->Lock();
this->h264Buffer->insert(h264Buffer->end(), buf, buf + size);
this->keyH264->Unlock();
}
//Read the data as much as the specified size from the buffer
int WvH264DataExtract::GetBuffers(BYTE * buf, int size)
{
int i;
if (size < 0)
{
return 0;
}
while (stop == false)
{
if (mp4Buffer->empty() == false && (int)mp4Buffer->size() > size)
{
keyMP4->Lock();
for (i = 0; i < size; i++)
{
buf[i] = (BYTE)mp4Buffer->front();
mp4Buffer->pop_front();
}
keyMP4->Unlock();
return size;
}
else
{
::Sleep(1);
continue;
}
}
return 0;
}
//Extract the SPS and PPS headers from the avcC box
int WvH264DataExtract::SPSPPS_extract(BYTE * buf, int index)
{
bool SPSflg = false;
bool PPSflg = false;
BYTE dat;
int i;
int cnt = 0;
int condition = SEARCH_SPSPPS;
int avcC_size = 0;
int SPSPPS_size = 0;
avcC_size = GetSize(buf + index, 4);
for (i = 4; i < avcC_size; i++)
{
21
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
dat = buf[index + i];
switch (condition)
{
case SEARCH_SPSPPS:
if (dat == 0x67)
{
WriteBuffers((BYTE*)hex_SCP, 4);
SPSPPS_size = GetSize(buf + (index + i - 2),
2);
condition = WRITE_SPS;
cnt = 0;
i--;
}
else if (dat == 0x68)
{
WriteBuffers((BYTE*)hex_SCP, 4);
SPSPPS_size = GetSize(buf + (index + i - 2),
2);
condition = WRITE_PPS;
cnt = 0;
i--;
}
break;
case WRITE_SPS:
WriteBuffer(dat);
cnt++;
if (cnt >= SPSPPS_size)
{
SPSflg = true;
condition = SEARCH_SPSPPS;
cnt = 0;
}
break;
case WRITE_PPS:
WriteBuffer(dat);
cnt++;
if (cnt >= SPSPPS_size)
{
PPSflg = true;
condition = SEARCH_SPSPPS;
cnt = 0;
}
break;
default:
printf("error : writing SPS or PPS\n");
return -1;
}
}
if(!SPSflg | !PPSflg)
{
printf("error : not found SPS or PPS\n");
return -1;
22
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
}
return index + i;
}
//Extract the NAL data from the mdat box mdat
int WvH264DataExtract::NAL_extract(BYTE * buf, int index, int mdat_size)
{
int nal_size = 0;
int mdat_size_count = 8;
while (mdat_size_count < mdat_size)
{
WriteBuffers((BYTE*)hex_SCP, 4);
nal_size = GetSize(buf + index, 4);
WriteBuffers(buf + index + 4, nal_size);
mdat_size_count += nal_size + 4;
index += nal_size + 4;
}
return index;
}
//Extract the NAL data from the H.264 video data acquired from the camera
UINT WvH264DataExtract::ExtractData()
{
int i;
int read_size = 0;
int buf_size = 0;
int bufBack_size = 0;
BYTE * buf = NULL;
BYTE * bufBack = NULL;
int mdat_size = 0;
bool SPSPPSfound = false;
UINT ret = 0;
printf("H.264 data extracting.");
while (stop == false)
{
read_size = 0;
buf_size = 1024;
buf = (BYTE*)malloc(buf_size);
if (bufBack_size > 0)
{
memcpy(buf, bufBack, bufBack_size);
free(bufBack);
bufBack = NULL;
}
if (GetBuffers(buf + bufBack_size, buf_size - bufBack_size) == 0)
{
break;
}
23
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
bufBack_size = 0;
if (SPSPPSfound == false)
{
//Extract the SPS and PPS hearders from the avcC box
for (i = 0; i <= buf_size - 4; i++)
{
if (buf[i] == hex_avcC[0] &&
buf[i + 1] == hex_avcC[1] &&
buf[i + 2] == hex_avcC[2] &&
buf[i + 3] == hex_avcC[3])
{
if ((read_size = SPSPPS_extract(buf, i - 4))
== -1)
{
ret = -1;
goto EXIT;
}
SPSPPSfound = true;
break;
}
}
}
//Search the mdat box and acquire the data size
for (i = read_size; i <= buf_size - 4; i++)
{
if (buf[i] == hex_mdat[0] &&
buf[i + 1] == hex_mdat[1] &&
buf[i + 2] == hex_mdat[2] &&
buf[i + 3] == hex_mdat[3])
{
mdat_size = GetSize(buf + i - 4, 4);
read_size = i + 4;
break;
}
}
if (i > buf_size - 4)
{
bufBack_size = 4;
bufBack = (BYTE*)malloc(bufBack_size);
memcpy(bufBack, buf + (buf_size - 4), bufBack_size);
continue;
}
//Acquire the data as much as the mdat box size from the buffer
if (buf_size - read_size >= mdat_size)
{
if (buf_size - read_size == mdat_size)
{
bufBack_size = 0;
}
else
{
24
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
bufBack_size = buf_size - (read_size + mdat_size);
bufBack = (BYTE*)malloc(bufBack_size);
memcpy(bufBack, buf + read_size + mdat_size,
bufBack_size);
}
}
else
{
bufBack_size = buf_size - read_size;
memmove(buf, buf + read_size, bufBack_size);
buf_size = mdat_size;
bufBack = (BYTE*)realloc(buf, buf_size);
buf = bufBack;
bufBack = NULL;
if (GetBuffers(buf + bufBack_size, buf_size - bufBack_size) ==
0)
{
break;
}
read_size = 0;
bufBack_size = 0;
}
//Extract the NAL data from the mdat box
read_size = NAL_extract(buf, read_size, mdat_size);
free(buf);
buf = NULL;
printf(".");
}
printf("\n");
printf("H.264 data extracted. \n");
EXIT:
if (buf)
{
free(buf);
buf = NULL;
}
if (bufBack)
{
free(bufBack);
bufBack = NULL;
}
return ret;
}
//Start the thread
UINT WvH264DataExtract::ThreadProc(LPVOID pParam)
25
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
{
WvH264DataExtract * extract = (WvH264DataExtract*)pParam;
return extract->ExtractData();
}
//Start the NAL data extraction processing
void WvH264DataExtract::StartExtractData(std::deque<BYTE> * mp4Buffer, std::deque<BYTE> *
h264Buffer, CCriticalSection * keyMP4, CCriticalSection * keyH264)
{
this->stop = false;
this->mp4Buffer = mp4Buffer;
this->h264Buffer = h264Buffer;
this->keyMP4 = keyMP4;
this->keyH264 = keyH264;
thread = AfxBeginThread(ThreadProc, this);
}
//End the NAL data extraction processing
void WvH264DataExtract::StopExtractData()
{
this->stop = true;
}
void WvH264DataExtract::WaitThreadProc()
{
WaitForSingleObject(thread->m_hThread, INFINITE);
}
26
1.4 Creating a File Save Class
Select “Project” > “Add Class” to open the “Add Class”” dialog. Select “C++ Class”, and then
press the Add button.
Enter “WvSaveFile” in the Class Name field, and then press the Finish button.
The following explains what this class performs.
・ The class generates a thread in which the following is performed: writing into the file the
NAL data extracted in the NAL data extraction class.
・ The thread starts with the StartSaveFile function and stops with the StopSaveFile
function.
The details of the file are as follows:
WvSaveFile.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma once
#include <deque>
#include <afxmt.h>
class WvSaveFile
{
public:
WvSaveFile(CString filepath);
~WvSaveFile(void);
private:
CString filepath; //Pass to a file to save to
CFile * out; //File operation object
//Flag for stopping the data saving procssing
bool stop;
std::deque<BYTE> * h264Buffer; //H.264 data buffer
//Object for the exclusive control of the H.264 data buffer
CCriticalSection * keyH264;
CWinThread * thread; //Object for the thread control
private:
static UINT ThreadProc(LPVOID pParam);
UINT SaveFile();
public:
void StartSaveFile(std::deque<BYTE> * h264Buffer, CCriticalSection * keyH264);
void StopSaveFile();
void WaitThreadProc();
};
27
WvSaveFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "StdAfx.h"
#include "WvSaveFile.h"
WvSaveFile::WvSaveFile(CString filepath)
{
this->stop = false;
this->h264Buffer = NULL;
this->keyH264 = NULL;
this->thread = NULL;
this->filepath = filepath;
}
WvSaveFile::~WvSaveFile(void)
{
this->h264Buffer = NULL;
this->keyH264 = NULL;
this->thread = NULL;
}
//Save the extracted NAL data into the file
UINT WvSaveFile::SaveFile()
{
int i;
char tmp;
//File open
try
{
out = new CFile(this->filepath.GetBuffer(),
CFile::modeCreate | CFile::modeWrite | CFile::typeBinary);
}
catch(CFileException * e)
{
CString err;
TCHAR buf[512];
e->GetErrorMessage(buf,512);
err.Format(_T("Error occurred : %d / %s\n"), e->m_cause, buf);
::AfxMessageBox(err, MB_OK | MB_ICONEXCLAMATION);
e->Delete();
return -1;
}
//Write the data in the buffer into the file
while (stop == false)
{
if (h264Buffer->empty() == false)
{
keyH264->Lock();
for (i = 0; i < (int)h264Buffer->size(); i++)
28
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{
tmp = (char)h264Buffer->front();
out->Write(&tmp, 1);
h264Buffer->pop_front();
}
keyH264->Unlock();
}
}
//Write the data remained in the buffer into the file
for (i = 0; i < (int)h264Buffer->size(); i++)
{
tmp = (char)h264Buffer->front();
out->Write(&tmp, 1);
h264Buffer->pop_front();
}
delete out;
return 0;
}
//Start the thread
UINT WvSaveFile::ThreadProc(LPVOID pParam)
{
WvSaveFile * savefile = (WvSaveFile*)pParam;
return savefile->SaveFile();
}
//Start the NAL data saving processing
void WvSaveFile::StartSaveFile(std::deque<BYTE> * h264Buffer, CCriticalSection * keyH264)
{
this->stop = false;
this->h264Buffer = h264Buffer;
this->keyH264 = keyH264;
thread = AfxBeginThread(ThreadProc, this);
}
//End the NAL data saving processing
void WvSaveFile::StopSaveFile()
{
this->stop = true;
}
void WvSaveFile::WaitThreadProc()
{
WaitForSingleObject(thread->m_hThread, INFINITE);
}
29
1.5 Combining the Classes
Using each class enables the implementation of the sample program for saving H.264 video
data.
The following explains what this class performs.
・ The class generates the objects of the WvCameraSession class, the
WvH264DataExtract class and the WvSaveFile class.
・ It starts a thread of each class, and then starts performing the processing for acquiring
the H.264 video data, extracting the NAL data and saving the extracted data.
・ The processing continues until the communication with camera ends after the lapse of a
specified duration or until the end event by the user occurs.
Notes on this sample program are as follows:
・ A buffer ring is used to pass data between the WvCameraSession and the
WvH264DataExtract and between the WvH264DataExtract class and the WvSaveFile
class, individually. A buffer is provided at a calling source to share the buffer between the
classes. When the buffer is operated, an exclusive control is performed with the
CCriticalSection.
・ After starting, the end of the thread of the WvCameraSession class is waited, and the
program goes into the standby status. When the communication with the camera ends,
the thread ends. Also, the press of the Ctrl and C or the end of command prompt is
detected, the StopGetVideo function is called to end the thread. After that, the
WvH264DataExtract class and the WvSaveFile class are stopped in that order so as to
process the data remained in the buffer.
30
Open the “SimpleH264DataExtract.h” and “SimpleH264DataExtract.cpp” files, and write as
follows:
SimpleH264DataExtract.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "resource.h"
#include "WvCameraSession.h"
#include "WvH264DataExtract.h"
#include "WvSaveFile.h"
std::deque<BYTE> mp4Buffer;
std::deque<BYTE> h264Buffer;
WvCameraSession * session;
WvH264DataExtract * extract;
WvSaveFile * savefile;
CCriticalSection keyMP4;
CCriticalSection keyH264;
SimpleH264DataExtract.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// SimpleH264DataExtract.cpp : Define the entry point of the console application
//
#include "stdafx.h"
#include "SimpleH264DataExtract.h"
#include "Windows.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//The one and only application object
CWinApp theApp;
using namespace std;
//Console event handler
BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT: //Press the Ctrl and C
case CTRL_CLOSE_EVENT: //Console end
session->StopGetVideo();
return TRUE;
}
return FALSE;
}
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure. if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: Change error code to suit your needs.
_tprintf(_T("Fatal error: MFC initialization failed \n"));
nRetCode = 1;
}
else
{
int duration = 0;
CString output = _T("C: \\h264.dat");
if (argc > 1)
{
for (int i = 1; i < argc; i++)
{
if(argv[i][0] == '-')
{
switch(argv[i][1]){
case 'o':
if (i + 1 < argc)
{
output = argv[i + 1];
}
break;
case 'd':
if (i + 1 < argc)
{
duration = _ttoi(argv[i
+ 1]);
}
break;
}
}
}
}
//Add the console event handler
BOOL ret = SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
if (ret == false)
{
return -1;
}
//Create the class object
session = new WvCameraSession(_T("192.168.100.1"), duration);
extract = new WvH264DataExtract();
savefile = new WvSaveFile(output);
32
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//Initialize the buffer
mp4Buffer.clear();
h264Buffer.clear();
//Start the thread
session->StartGetVideo(&mp4Buffer, &keyMP4);
extract->StartExtractData(&mp4Buffer, &h264Buffer, &keyMP4, &keyH264);
savefile->StartSaveFile(&h264Buffer, &keyH264);
//Monitor the camera communication thread
session->WaitThreadProc();
//Stop the thread
extract->StopExtractData();
extract->WaitThreadProc();
savefile->StopSaveFile();
savefile->WaitThreadProc();
//Discard the class object
delete session;
delete extract;
delete savefile;
}
return nRetCode;
}