h.264 tutorial yt1-1627-000

32
NETWORK CAMERA Protocol Spec. H.264 Data Protocol Tutorial Ver. 1.0 YT1-1627-000 April 20, 2011 CANON INC.

Upload: tapi5612

Post on 21-Apr-2015

42 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 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.

Page 2: H.264 Tutorial YT1-1627-000

2

Change Tracking List

Version Date Revised page Note

Ver. 1.0 April 20, 2011 - -

Page 3: H.264 Tutorial YT1-1627-000

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

Page 4: H.264 Tutorial YT1-1627-000

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.

Page 5: H.264 Tutorial YT1-1627-000

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.

Page 6: H.264 Tutorial YT1-1627-000

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.

Page 7: H.264 Tutorial YT1-1627-000

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.

Page 8: H.264 Tutorial YT1-1627-000

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.

Page 9: H.264 Tutorial YT1-1627-000

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)

Page 10: H.264 Tutorial YT1-1627-000

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();

};

Page 11: H.264 Tutorial YT1-1627-000

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();

}

Page 12: H.264 Tutorial YT1-1627-000

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

}

Page 13: H.264 Tutorial YT1-1627-000

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

}

Page 14: H.264 Tutorial YT1-1627-000

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)

Page 15: H.264 Tutorial YT1-1627-000

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)

Page 16: H.264 Tutorial YT1-1627-000

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.

Page 17: H.264 Tutorial YT1-1627-000

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();

};

Page 18: H.264 Tutorial YT1-1627-000

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

{

Page 19: H.264 Tutorial YT1-1627-000

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)

{

Page 20: H.264 Tutorial YT1-1627-000

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++)

{

Page 21: H.264 Tutorial YT1-1627-000

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;

Page 22: H.264 Tutorial YT1-1627-000

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;

}

Page 23: H.264 Tutorial YT1-1627-000

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

{

Page 24: H.264 Tutorial YT1-1627-000

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)

Page 25: H.264 Tutorial YT1-1627-000

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

}

Page 26: H.264 Tutorial YT1-1627-000

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();

};

Page 27: H.264 Tutorial YT1-1627-000

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++)

Page 28: H.264 Tutorial YT1-1627-000

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

}

Page 29: H.264 Tutorial YT1-1627-000

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.

Page 30: H.264 Tutorial YT1-1627-000

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;

}

Page 31: H.264 Tutorial YT1-1627-000

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

Page 32: H.264 Tutorial YT1-1627-000

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;

}