This code is a sample DVR example (own filesystem) made a long time(15 years) ago. This is a DVR example made only with SW like FFMPEG so that ordinary people such as PCs, camcorders, and graphics cards can follow it without HW codecs at the time. I hope it will be helpful to students studying file system and video.
1. Linux PC 2. Web Cam 3. Matrox Graphic card
1. FFMPEG 2. DirectFB 3. V4L
1. Private FileSystem
- Structure of Storage File System
- Structure of LVG
- LVG Writing
- LVG Linked List
- Structure of Image
- Image Arrangement in Record Data
- Image Detail packet
- Summarize
- HDD Offset 표기
A. Byte 단위
B. BLock 단위 : 512 Byte
C. Source 상에서는 Block 단위의 Addressing을 주로 사용하고 있음.
D. Data align에 따른 ACcess 효율 향상
E. 숫자 크기를 작게 유지 - byte 단위의 제약 : 32bit 정수로 표현할 경우 2 TB (signed), 4 TB (unsigned) 용량 제한
그러나, Block 단위일 경우 512 배까지의 Addressing 가능
- HDD에 직접 access (_llseek) 하는 단계에서는 byte 단위로 addressing 함 (64 bit크기)
- HDD Linear address 상의 정보 배치표
Linear Address
A : HEADER 영역 (0 ~5 GB)
B : Reserved (약 100MB ~ 약 4800 MB)
사용하지 않고 일부러 비워둔 영역의 의미가 아니라, 실제로는 4.8GB 정도의 파티션이 만들어져 있어서 COPY 시 임시 파일 생성 용으로 사용됨.
- fdisk 유틸리티 상의 단위 12 ~ 600
C : DATA 영역 (5GB 이후 ~)
Reserved 영역과 HEADER 영역을 제외한 나머지의 공간은 1GB 씩 분할되어 DVR 녹화 데이터 저장용으로 사용됨.
(LVG = Logical Volume Group)
- HEADER 영역
HEADER 영역
가. NEW_HDD_HEADER_OFFSET (= 5MB)
typedef struct {
char signature[8]; // "Oriong"
int num_volumes; // 250 GB HDD의 경우, 약 240
UINT64 time_signature; // unused
int num_bad_volumes; // Playback 할 떄 갱신함
char reserved1[36];
} NEW_HDD_HEADER;
나. HDD_MIRROR_INFO_OFFSET (= 5.5 MB)
typedef struct {
DWORD mirror_signature1; // 'M'
UINT64 mirrored_time_signature; // 미러링 생성시 시간 정보
UINT64 time_signature_of_pair; // unused
DWORD mirror_signature2; // 'M'의 bitwise negative
} MIRROR_INFO_HEADER;
다. BAD_LVGS_OFFSET (= 6 MB)
1GB 당 1 byte 씩 할당된 정보를 저장. 이 값이 '3'인 경우 해당 영역은 BAD LVG.
단, NEW_HDD_HEADERdml num_bad_volumes가 0 인 경우, 이 부분의 DATA를 참조해서는 안 됨.
BAD_BLOCK이 발생하는 순간부터 이 BLOCK이 속한 LVG DATA를 기록하기 시작함.
라. NEW_HDD_HEADER_BACKUP_OFFSET (= 10 MB)
NEW_HDD_HEADER_OFFSET에 기록된 내용과 동일한 내용이 기록되어 있음 (BACKUP)
- DATA 영역
240 개의 LVG가 존재하는 HDD의 예시
- 1GB 씩 분할되어 있으며, 1 GB 미만의 잔여 영역은 사용하지 않음.
- LVG 내에서는 데이터가 시간 순서대로 저장 되어 있음. (가정)
- LVG 간에는 시간 순서가 보장되지 않음.
- 초기에는 물리적인 순서대로 데이터를 채워 나가므로, 정렬되어 있으나 DISK의 추가나 교체, Repeat Record 여부에 따라 시간 순서가 뒤바뀔 수 있음.
- 내장 HDD의 물리적인 순서
- HDD1 -> HDD3 -> HDD2-> HDD4 = Master (Primary -> Secondary) -> Slave (Primary -> Secondary)
- 시작(부팅)할 때, LVG들을 Sorting
- LVG 구성 (1 GB)
가. superblock (= 0 MB)
typedef struct {
int volume_id;
int status; // 0: Empty, 3:BAD, 1,2:DATA
time_t start_time;
time_t end_time;
DWORD last_offset;
DWORD new_offset;
int event_count; // 해당 LVG 내의 event 개수
}
나. LVG_TIMEINDEX_OFFSET (= 1 MB)
- 1초당 4byte + 4 byte (총 64 bit)씩의 index (little endian 방식으로 저장)
- 최대 384 KB : 49152초 = 약 13.65 시간
- 특정 시간 (sec)의 정보를 읽는 방법 (TIME SEARCH)
(void *)((target_second - LOGICAL_VOLUME_HEADER.start_time) * 8)
다. LVG_EVENTINDEX_OFFSET (= 2.5 MB)
struct tagEventInfo {
time_t datetime;
U32 group_offset;
U8 group_index;
U8 type;
U8 channel;
U8 record_exist;
};
이벤트 1건당 12 byte 씩.
해당 LVG의 이벤트 정보의 크기 = LOGICAL_VOLUME_HEADER.event_count * 12
라. 해당 LVG의 녹화 데이터 저장 영역 (4 MB ~ )
- 녹화 데이터 저장 영역
GROUP
- 영상 / 음성 데이터의 집합
- 검색의 기본 단위 : 초 (SEC) 단위로 GROUP 이 구분되어짐.
- 한 GROUP은 1 MB 이내
- 한 GROUP의 데이터 개수는 120개 이내
- 1 MB를 초과하거나 120개의 데이터를 초과하면 1초 내에 2개 이상의 GROUP이 생성됨.
typedef struct {
WORD CH_exist; // CH별 녹화 데이터 존재 여부
WORD I_exist; // CH별 I-picture 존재 여부
WORD block_size; // GROUP size (block)
WORD prev_blocks; // prev GROUP size (block)
WORD num_picture; // 데이터 개수
BYTE index_high[MAX_IMAGE_SIZE]; // 각 데이터 위치 1
WORD index_low[MAX_IMAGE_SIZE]; // 각 데이터 위치 2
DWORD Izone; // I picture size (byte)
DWORD non_Izone; // non-I picture size (byte)
DWORD prev_offset; // prev GROUP offset (block)
DWORD current_offset; // current GROUP offset (block)
DWORD next_offset; // next GROUP offset (block)
BYTE CH_count; // unsused
BYTE reserved1;
WORD EVENT_exist; // CH별 이벤트 존재 여부
time_t last_time; // 현재 그룹의 녹화 시간
DWORD seq; // GROUP 일련 번호 (검증용)
BYTE status; // NO USE
struct tagEventInfo *event_offset_start;
struct tagEventInfo *event_offset_end;
int nr_events; // GROUP 내 이벤트 개수
time_t lvg_time; // 현재 LVG의 고유값
DWORD version; // 현재 구조체의 버전
} NEW_GROUP_HEADER;
HEADER, TAIL1, TAIL2 는 모두 512 byte 크기로 고정되어 있고,
TAIL1의 하위 256 byte에는 채널명이 저장되어 있음 (16 byte X 16 Channel)
TAIL2에는 별도의 정보가 저장되어 있지 않음.
- PICTURE 데이터 구조
DATA
- Group Header의 Index는 sequentail 하지만, DATA 저장 순서는 sequentail 하지 않음.
- I zone 영역에는 Video I picture 만 저장됨.
- non-I zone 영역에는 기타 Video, Audio, Text가 저장됨
typedef struct {
time_t datetime; // 녹화 시간
DWORD version; // 구조체 버전
int size; // payload 크기
void* event_offset; // unused
time_t timezone_offset; // GMT +,-
DWORD reserved10_1;
WORD reserved10_2;
BYTE data_type; // VIDEO, AUDIO, TEXT
BYTE picture_type; // I, P, B
BYTE channel; // 채널 번호
BYTE event_type; // 이벤트 종류
BYTE NtscPal; // 0:NTSC, 1:PAL
BYTE frame_rate; // frame rate
DWORD seq; // Video 순서
DWORD I_seq; // I picture 순서
BYTE resolution; // 해상도
BYTE normal_rec; // NORMAL REC
BYTE event_rec; 이벤트 REC
BYTE reserved1;
BYTE event_msg; // 이벤트 메시지 종류
BYTE isdst; // 써머 타임 active 여부
- LVG Writing 포멧 후 두개의 쓰레드를 이용하여 1초마다 데이터를 HDD에 Write 한다.
- free_LVG에서 하나의 pLVG를 꺼내 data_LVG에 넣는다.
- gNewVar.Record.LVG_ptr에 위에서 꺼낸 pLVG를 복사한다.
- Thread A는 image들을 prebuffer 버퍼에 넣는다.
- Thread B는 1초마다 prebuffer 버퍼의 내용을 gNewVar.Record.pData에 재구성하고 다시 write_cached_buffer() 함수를 통해서 HDD에 Write 한다.
- gNewVar.Record.pTimeIndex에 write한 Data의 Offset(Position)을 기록한다. gNewVar.Record.pTimeIndex 배열은 768 X 512의 크기이고 이는 13.653시간 정도이다.
- gNewVar.Record.pTimeIndex 배열은 512 크기로 1초마다 HDD에 Write 되는데 이 구간은 0, 512, 1024...와 같이 배열의 처음부터 512 크기로 증가하여 HDD에 write 한다. 우선 Thread B에서 배열에 초당 8 byte를 쓴다. 위 배열에는 64초가 지나야 배열의 512가 모두 채워지나 초당 512 byte를 HDD write하므로 64초 이내에는 중복으로 적는 값이 존재하게 된다.
BYTE watermark; // 워터마크 적용 여부
BYTE crc_value;
} IMAGE_HEADER;
- Image 배치구조










