From 2ddfc43bc261a8dde9642631b8c7cd50b71741ea Mon Sep 17 00:00:00 2001 From: mjbudd77 Date: Thu, 2 Sep 2021 21:13:54 -0400 Subject: [PATCH] Implemented first cut at avi 2.0 indexing for Qt GUI. --- src/drivers/Qt/avi/avi-utils.cpp | 273 ++++++++++++++++++++++++++++++- src/drivers/Qt/avi/fileio.cpp | 51 +++++- src/drivers/Qt/avi/gwavi.cpp | 33 +++- src/drivers/Qt/avi/gwavi.h | 24 ++- 4 files changed, 362 insertions(+), 19 deletions(-) diff --git a/src/drivers/Qt/avi/avi-utils.cpp b/src/drivers/Qt/avi/avi-utils.cpp index a06f195f..e7fcf348 100644 --- a/src/drivers/Qt/avi/avi-utils.cpp +++ b/src/drivers/Qt/avi/avi-utils.cpp @@ -342,13 +342,18 @@ gwavi_t::write_avi_header_chunk(FILE *fp) if (write_chars_bin(fp, "LIST", 4) == -1) goto write_chars_bin_failed; + if ((marker = ftell(fp)) == -1) goto ftell_failed; + if (write_int(fp, 0) == -1) goto write_int_failed; + if (write_chars_bin(fp, "hdrl", 4) == -1) goto write_chars_bin_failed; - if (write_avi_header(fp, &avi_header) == -1) { + + if (write_avi_header(fp, &avi_header) == -1) + { (void)fprintf(stderr, "write_avi_header_chunk: " "write_avi_header() failed\n"); return -1; @@ -356,69 +361,108 @@ gwavi_t::write_avi_header_chunk(FILE *fp) if (write_chars_bin(fp, "LIST", 4) == -1) goto write_chars_bin_failed; + if ((sub_marker = ftell(fp)) == -1) goto ftell_failed; + if (write_int(fp, 0) == -1) goto write_int_failed; + if (write_chars_bin(fp, "strl", 4) == -1) goto write_chars_bin_failed; - if (write_stream_header(fp, &stream_header_v) == -1) { + + if (write_stream_header(fp, &stream_header_v) == -1) + { (void)fprintf(stderr, "write_avi_header_chunk: " "write_stream_header failed\n"); return -1; } - if (write_stream_format_v(fp, &stream_format_v) == -1) { + if (write_stream_format_v(fp, &stream_format_v) == -1) + { (void)fprintf(stderr, "write_avi_header_chunk: " "write_stream_format_v failed\n"); return -1; } + if ( avi_std >= 2 ) + { + stream_index_v.fpos = ftell(fp); + + if ( write_stream_super_indx(fp, &stream_index_v ) == -1 ) + { + return -1; + } + } if ((t = ftell(fp)) == -1) goto ftell_failed; if (fseek(fp, sub_marker, SEEK_SET) == -1) goto fseek_failed; + if (write_int(fp, (unsigned int)(t - sub_marker - 4)) == -1) goto write_int_failed; + if (fseek(fp, t, SEEK_SET) == -1) goto fseek_failed; - if (avi_header.data_streams == 2) { + if (avi_header.data_streams == 2) + { if (write_chars_bin(fp, "LIST", 4) == -1) goto write_chars_bin_failed; + if ((sub_marker = ftell(fp)) == -1) goto ftell_failed; + if (write_int(fp, 0) == -1) goto write_int_failed; + if (write_chars_bin(fp, "strl", 4) == -1) goto write_chars_bin_failed; - if (write_stream_header(fp, &stream_header_a) == -1) { + + if (write_stream_header(fp, &stream_header_a) == -1) + { (void)fprintf(stderr, "write_avi_header_chunk: " "write_stream_header failed\n"); return -1; } - if (write_stream_format_a(fp, &stream_format_a) == -1) { + if (write_stream_format_a(fp, &stream_format_a) == -1) + { (void)fprintf(stderr, "write_avi_header_chunk: " "write_stream_format_a failed\n"); return -1; } + if ( avi_std >= 2 ) + { + stream_index_a.fpos = ftell(fp); + + if ( write_stream_super_indx(fp, &stream_index_a ) == -1 ) + { + return -1; + } + } if ((t = ftell(fp)) == -1) goto ftell_failed; + if (fseek(fp, sub_marker, SEEK_SET) == -1) goto fseek_failed; + if (write_int(fp, (unsigned int)(t - sub_marker - 4)) == -1) goto write_int_failed; + if (fseek(fp, t, SEEK_SET) == -1) goto fseek_failed; } if ((t = ftell(fp)) == -1) goto ftell_failed; + if (fseek(fp, marker, SEEK_SET) == -1) goto fseek_failed; + if (write_int(fp, (unsigned int)(t - marker - 4)) == -1) goto write_int_failed; + if (fseek(fp, t, SEEK_SET) == -1) goto fseek_failed; @@ -463,8 +507,223 @@ int gwavi_t::peak_chunk( FILE *fp, long int idx, char *fourcc, unsigned int *siz return 0; } +int gwavi_t::write_stream_super_indx(FILE *fp, struct gwavi_super_indx_t *indx) +{ + long long t, sizeMarker; + + if (write_chars_bin(fp, "indx", 4) == -1) // FCC + { + (void)fprintf(stderr, "write_index: write_chars_bin) failed\n"); + return -1; + } + if ((sizeMarker = ftell(fp)) == -1) // size of this chunk + { + perror("write_index (ftell)"); + return -1; + } + if (write_int(fp, 0) == -1) + return -1; + + if (write_short(fp, 4) == -1) // wLongsPerEntry; // must be 4 (size of each entry in aIndex array) + return -1; + + if (write_byte(fp, 0) == -1) // bIndexSubType; // must be 0 or AVI_INDEX_2FIELD + return -1; + + if (write_byte(fp, 0) == -1) // bIndexType; // must be AVI_INDEX_OF_INDEXES + return -1; + + if (write_int(fp, indx->nEntriesInUse) == -1) // nEntriesInUse; // number of entries in aIndex array that + return -1; + + if (write_chars_bin(fp, indx->chunkId, 4) == -1) // dwChunkId; // ’##dc’ or ’##db’ or ’##wb’, etc + return -1; + + for (int i=0; i<3; i++) + { + if (write_int(fp, 0) == -1) // dwReserved[3]; // must be 0 + return -1; + } + + for (int i=0; i<32; i++) + { + if (write_int64(fp, indx->aIndex[i].qwOffset) == -1) //qwOffset; // absolute file offset, offset 0 is + return -1; + + if (write_int(fp, indx->aIndex[i].dwSize) == -1) // dwSize; // size of index chunk at this offset + return -1; + + if (write_int(fp, indx->aIndex[i].dwDuration) == -1) // dwDuration; // time span in stream ticks + return -1; + } + + if ((t = ftell(fp)) == -1) + { + perror("write_index (ftell)"); + return -1; + } + if (fseek(fp, sizeMarker, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + if (write_int(fp, (unsigned int)(t - sizeMarker - 4)) == -1) + return -1; + + if (fseek(fp, t, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + return 0; +} + +int gwavi_t::write_stream_std_indx(FILE *fp, struct gwavi_super_indx_t *indx) +{ + char fcc[8]; + long long t, sizeMarker, std_indx_ofs, qwBaseOffset = 0; + unsigned int chunkSize = 0, numEntries = 0; + + std_indx_ofs = ftell(fp); + + sprintf( fcc, "ix%02i", indx->streamId ); + + if (write_chars_bin(fp, fcc, 4) == -1) // FCC + { + (void)fprintf(stderr, "write_index: write_chars_bin) failed\n"); + return -1; + } + if ((sizeMarker = ftell(fp)) == -1) // size of this chunk + { + perror("write_index (ftell)"); + return -1; + } + if (write_int(fp, chunkSize) == -1) + return -1; + + if (write_short(fp, 2) == -1) // wLongsPerEntry; // must be 2 (sizeof(aIndex[0])/sizeof(DWORD)) + return -1; + + if (write_byte(fp, 0) == -1) // bIndexSubType; // must be 0 + return -1; + + if (write_byte(fp, 0x01) == -1) // bIndexType; // must be AVI_INDEX_OF_CHUNKS + return -1; + + if (write_int(fp, numEntries) == -1) // nEntriesInUse; // number of entries in aIndex array that + return -1; + + if (write_chars_bin(fp, indx->chunkId, 4) == -1) // dwChunkId; // ’##dc’ or ’##db’ or ’##wb’, etc + return -1; + + if (write_int64(fp, qwBaseOffset) == -1) //qwBaseOffset; // all dwOffsets in aIndex array are relative to this + return -1; + + if (write_int(fp, 0) == -1) // dwReserved3; // must be 0 + return -1; + + for (size_t i=0; istreamId ) + { + continue; + } + if ( qwBaseOffset == 0 ) + { + qwBaseOffset = offsets[i].fofs; + } + dwOffset = offsets[i].fofs - qwBaseOffset + 8; + + if (write_int(fp, dwOffset) == -1) // qwBaseOffset + this is absolute file offset + return -1; + + dwSize = offsets[i].len; + + if ( !offsets[i].keyFrame ) + { + dwSize |= 0x80000000; + } + + if (write_int(fp, dwSize) == -1) // bit 31 is set if this is NOT a keyframe + return -1; + + numEntries++; + } + + if ((t = ftell(fp)) == -1) + { + perror("write_index (ftell)"); + return -1; + } + if (fseek(fp, sizeMarker, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + chunkSize = t - sizeMarker - 4; + + if (write_int(fp, chunkSize) == -1) + return -1; + + if (write_short(fp, 2) == -1) // wLongsPerEntry; // must be 2 (sizeof(aIndex[0])/sizeof(DWORD)) + return -1; + + if (write_byte(fp, 0) == -1) // bIndexSubType; // must be 0 + return -1; + + if (write_byte(fp, 0x01) == -1) // bIndexType; // must be AVI_INDEX_OF_CHUNKS + return -1; + + if (write_int(fp, numEntries) == -1) // nEntriesInUse; // number of entries in aIndex array that + return -1; + + if (write_chars_bin(fp, indx->chunkId, 4) == -1) // dwChunkId; // ’##dc’ or ’##db’ or ’##wb’, etc + return -1; + + if (write_int64(fp, qwBaseOffset) == -1) //qwBaseOffset; // all dwOffsets in aIndex array are relative to this + return -1; + + if (fseek(fp, t, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + + if ( indx->nEntriesInUse < 32 ) + { + int i = indx->nEntriesInUse; + + indx->aIndex[i].qwOffset = std_indx_ofs; + indx->aIndex[i].dwSize = chunkSize; + indx->aIndex[i].dwDuration = 0; + + indx->nEntriesInUse++; + } + + if (fseek(fp, indx->fpos, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + + if ( write_stream_super_indx( fp, indx ) == -1 ) + { + return -1; + } + + if (fseek(fp, t, SEEK_SET) == -1) + { + perror("write_index (fseek)"); + return -1; + } + + return 0; +} + int -gwavi_t::write_index(FILE *fp) +gwavi_t::write_index1(FILE *fp) { long long marker, t; unsigned int offset = 4; diff --git a/src/drivers/Qt/avi/fileio.cpp b/src/drivers/Qt/avi/fileio.cpp index 89e5a778..838379a7 100644 --- a/src/drivers/Qt/avi/fileio.cpp +++ b/src/drivers/Qt/avi/fileio.cpp @@ -61,10 +61,16 @@ gwavi_t::write_int(FILE *out, unsigned int n) { unsigned char buffer[4]; - buffer[0] = n; - buffer[1] = n >> 8; - buffer[2] = n >> 16; - buffer[3] = n >> 24; + for (int i=0; i<4; i++) + { + buffer[i] = (n & 0x000000FF); + + n = n >> 8; + } + //buffer[0] = n; + //buffer[1] = n >> 8; + //buffer[2] = n >> 16; + //buffer[3] = n >> 24; if (fwrite(buffer, 1, 4, out) != 4) return -1; @@ -72,13 +78,46 @@ gwavi_t::write_int(FILE *out, unsigned int n) return 0; } +int +gwavi_t::write_int64(FILE *out, unsigned long long int n) +{ + unsigned char buffer[8]; + + for (int i=0; i<8; i++) + { + buffer[i] = (n & 0x000000FFllu); + + n = n >> 8; + } + + if (fwrite(buffer, 1, 8, out) != 8) + return -1; + + return 0; +} + +int +gwavi_t::write_byte(FILE *out, unsigned char n) +{ + if (fwrite( &n, 1, 1, out) != 1) + return -1; + + return 0; +} + int gwavi_t::write_short(FILE *out, unsigned int n) { unsigned char buffer[2]; - buffer[0] = n; - buffer[1] = n >> 8; + for (int i=0; i<2; i++) + { + buffer[i] = (n & 0x000000FF); + + n = n >> 8; + } + //buffer[0] = n; + //buffer[1] = n >> 8; if (fwrite(buffer, 1, 2, out) != 2) return -1; diff --git a/src/drivers/Qt/avi/gwavi.cpp b/src/drivers/Qt/avi/gwavi.cpp index 9b848190..cc714be6 100644 --- a/src/drivers/Qt/avi/gwavi.cpp +++ b/src/drivers/Qt/avi/gwavi.cpp @@ -73,11 +73,13 @@ gwavi_t::gwavi_t(void) memset( &stream_format_v, 0, sizeof(struct gwavi_stream_format_v_t) ); memset( &stream_header_a, 0, sizeof(struct gwavi_stream_header_t) ); memset( &stream_format_a, 0, sizeof(struct gwavi_stream_format_a_t) ); + memset( &stream_index_v , 0, sizeof(struct gwavi_super_indx_t) ); + memset( &stream_index_a , 0, sizeof(struct gwavi_super_indx_t) ); memset( fourcc, 0, sizeof(fourcc) ); marker = 0; movi_fpos = 0; bits_per_pixel = 24; - + avi_std = 2; } gwavi_t::~gwavi_t(void) @@ -205,6 +207,9 @@ gwavi_t::open(const char *filename, unsigned int width, unsigned int height, stream_format_v.palette = 0; stream_format_v.palette_count = 0; + strcpy( stream_index_v.chunkId, "00dc"); + stream_index_v.streamId = 0; + if (audio) { /* set stream header */ @@ -232,6 +237,9 @@ gwavi_t::open(const char *filename, unsigned int width, unsigned int height, audio->channels * (audio->bits / 8); stream_format_a.bits_per_sample = audio->bits; stream_format_a.size = 0; + + strcpy( stream_index_a.chunkId, "01wb"); + stream_index_a.streamId = 1; } if (write_chars_bin(out, "RIFF", 4) == -1) @@ -312,6 +320,7 @@ gwavi_t::add_frame( unsigned char *buffer, size_t len, unsigned int flags) //printf("Frame Offset: %li\n", ftell(out) - movi_fpos ); + idx.fofs = ftell(out); idx.len = len; idx.type = 0; idx.keyFrame = (flags & IF_KEYFRAME) ? 1 : 0; @@ -374,6 +383,7 @@ gwavi_t::add_audio( unsigned char *buffer, size_t len) maxi_pad = WORD_SIZE - maxi_pad; } + idx.fofs = ftell(out); idx.len = len; idx.type = 1; idx.keyFrame = 1; @@ -438,13 +448,26 @@ gwavi_t::close(void) if (fseek(out,t,SEEK_SET) == -1) goto fseek_failed; - if (write_index(out) == -1) + if ( avi_std < 2 ) { - (void)fprintf(stderr, "gwavi_close: write_index() failed\n"); - return -1; + if (write_index1(out) == -1) + { + (void)fprintf(stderr, "gwavi_close: write_index() failed\n"); + return -1; + } + } + else + { + if ( write_stream_std_indx( out, &stream_index_v ) == -1 ) + { + return -1; + } + if ( write_stream_std_indx( out, &stream_index_a ) == -1 ) + { + return -1; + } } - //free(offsets); offsets.clear(); /* reset some avi header fields */ diff --git a/src/drivers/Qt/avi/gwavi.h b/src/drivers/Qt/avi/gwavi.h index 3686349a..0cdb9243 100644 --- a/src/drivers/Qt/avi/gwavi.h +++ b/src/drivers/Qt/avi/gwavi.h @@ -142,8 +142,23 @@ struct gwavi_audio_t unsigned int samples_per_second; }; +struct gwavi_super_indx_t +{ + unsigned long long fpos; + unsigned int nEntriesInUse; + char chunkId[7]; + char streamId; + + struct { + unsigned long long qwOffset; + unsigned int dwSize; + unsigned int dwDuration; + } aIndex[32]; +}; + struct gwavi_index_rec_t { + long long fofs; unsigned int len; unsigned char type; unsigned char keyFrame; @@ -189,12 +204,15 @@ class gwavi_t struct gwavi_header_t avi_header; struct gwavi_stream_header_t stream_header_v; struct gwavi_stream_format_v_t stream_format_v; + struct gwavi_super_indx_t stream_index_v; struct gwavi_stream_header_t stream_header_a; struct gwavi_stream_format_a_t stream_format_a; + struct gwavi_super_indx_t stream_index_a; long long marker; std::vector offsets; long long movi_fpos; int bits_per_pixel; + int avi_std; char fourcc[8]; // helper functions @@ -207,10 +225,14 @@ class gwavi_t struct gwavi_stream_format_v_t *stream_format_v); int write_stream_format_a(FILE *fp, struct gwavi_stream_format_a_t *stream_format_a); + int write_stream_std_indx(FILE *fp, struct gwavi_super_indx_t *indx); + int write_stream_super_indx(FILE *fp, struct gwavi_super_indx_t *indx); int write_avi_header_chunk( FILE *fp ); - int write_index(FILE *fp); + int write_index1(FILE *fp); int check_fourcc(const char *fourcc); int write_int(FILE *fp, unsigned int n); + int write_int64(FILE *out, unsigned long long int n); + int write_byte(FILE *fp, unsigned char n); int write_short(FILE *fp, unsigned int n); int write_chars(FILE *fp, const char *s); int write_chars_bin(FILE *fp, const char *s, int count);