You are on page 1of 32

NETWORK CAMERA Protocol Spec. H.

264 Data Protocol Tutorial


Ver. 1.0 YT1-1627-000

April 20, 2011 CANON INC.

Change Tracking List


Version Ver. 1.0 Date April 20, 2011 Revised page Note -

Table of Contents
1 Sample Program for Saving H.264 Video Data .......................................................................... 4 1.1 1.2 1.3 1.4 1.5 Creating a Project .................................................................................................... 6 Creating a Camera Communication Class .............................................................. 8 Creating a NAL Data Extraction Class................................................................... 14 Creating a File Save Class .................................................................................... 26 Combining the Classes .......................................................................................... 29

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.

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 cameras 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.

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.

Check the MFC checkbox under Add common header files for in Application Settings of the Win32 Application Wizard, and then press the Finish button.

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.

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)

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; //Duration in which an image is transmitted int duration; bool stop; std::deque<BYTE> * mp4Buffer; //Object for the exclusive control of the CCriticalSection * keyMP4; CWinThread * thread; private: static UINT ThreadProc(LPVOID pParam); UINT GetVideo(); public: void StartGetVideo(std::deque<BYTE> * mp4Buffer, CCriticalSection * keyMP4); void StopGetVideo(); void WaitThreadProc(); }; //Host name of the camera from the camera(sec.) //Flag for stopping the image data //MP4 data buffer MP4 data buffer //Object for the thread control

10

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 #include #include #include "StdAfx.h" "WvCameraSession.h" <afxinet.h> <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(); }

11

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

12

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

13

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)

14

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)

15

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.

16

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

17

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 {

18

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

19

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

20

162 163 164 165 166 167 168 169 170 2); 171 172 173 174 175 176 177 178 2); 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), condition = WRITE_SPS; cnt = 0; i--; } else if (dat == 0x68) { WriteBuffers((BYTE*)hex_SCP, 4); SPSPPS_size = GetSize(buf + (index + i - 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;

21

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

22

268 269 270 271 272 273 274 275 276 277 278 279 280 == -1) 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)) { 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 {

23

321 322 323 bufBack_size); 324 325 326 327 328 329 330 331 332 333 334 335 336 0) 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 { } } else {

bufBack_size = buf_size - (read_size + mdat_size); bufBack = (BYTE*)malloc(bufBack_size); memcpy(bufBack, buf + read_size + mdat_size,

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

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)

24

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

25

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

26

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

27

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

28

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.

29

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 #include #include #include "resource.h" "WvCameraSession.h" "WvH264DataExtract.h" "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; }

30

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

31

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

32

You might also like