int riff_stack_streamwrite(riff_stack *s,riff_chunk *c,const void *buf,size_t len) { if (s->write == NULL) return -1; if (c) { if (!c->wmode) return -1; /* once the data is written, you are not allowed to write any more */ if (c->write_offset != 0) return -1; /* AVI chunks are limited to 2GB or less */ if ((c->write_offset+len) >= 0x80000000LL) return -1; /* assume the write will complete and setup pointers now */ c->read_offset = c->write_offset = len; c->data_length = (uint32_t)c->write_offset; c->absolute_data_length = (c->data_length + 1UL) & (~1UL); /* write the header NOW */ riff_stack_header_sync(s,c); /* then write the data. * the consequence of this design is that unlike riff_stack_write() the * call is considered an absolute failure IF we were not able to write all * the data to disk. We have to, the design of this code bets on it! * * NTS: We allow the caller to streamwrite NULL-length packets with buf=NULL and len=0 */ if (buf != NULL) { int rd; if (s->seek(s,c->absolute_data_offset) != c->absolute_data_offset) return 0; rd = s->write(s,buf,len); /* if we were not able to write all data, well, too bad. update the header */ if (rd < (int)len) { if (rd < 0) rd = 0; c->read_offset = c->write_offset = rd; c->data_length = (uint32_t)c->write_offset; c->absolute_data_length = (c->data_length + 1UL) & (~1UL); /* write the header NOW */ riff_stack_header_sync(s,c); /* fail */ return -1; } } /* we already wrote the header, and the caller is SUPPOSED to * use this function ONCE for a RIFF chunk. Don't let riff_stack_pop() * waste it's time lseek()'ing back to rewrite the header */ c->disable_sync = 1; } else { abort(); /* TODO */ } return len; }
/* NTS: this code makes no attempt to optimize chunk sizes and combine samples/frames---if you're * stupid enough to call this routine with 4-byte long data then 4-byte long chunks is what * you'll get, don't blame me if doing that overruns the allocated space set aside for the * indexes. */ int avi_writer_stream_write(avi_writer *w,avi_writer_stream *s,void *data,size_t len,uint32_t flags) { avi_writer_stream_index *si; riff_chunk chunk; if (w == NULL || s == NULL) return 0; if (w->state != AVI_WRITER_STATE_BODY) return 0; /* calling this function with data == NULL is perfectly valid, it simply means no data */ if (data == NULL) len = 0; /* make sure we're down into the 'movi' chunk */ while (w->riff->current > 1) riff_stack_pop(w->riff); /* make sure this is the movi chunk */ if (w->riff->current != 1) return 0; if (w->riff->top->fourcc != avi_riff_movi) return 0; if (w->enable_opendml) { /* if we're writing an OpenDML 2.0 compliant file, and we're approaching a movi size of 1GB, * then split the movi chunk and start another RIFF:AVIX */ if (((unsigned long long)w->riff->top->write_offset + (unsigned long long)len) >= 0x3FF00000ULL) { /* 1GB - 16MB */ riff_stack_writing_sync(w->riff); /* sync all headers and pop all chunks */ assert(w->riff->current == -1); /* should be at top level */ /* at the first 1GB boundary emit AVIOLDINDEX for older AVI applications */ if (w->group == 0 && w->enable_avioldindex) avi_writer_emit_avioldindex(w); /* [1] RIFF:AVIX */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_list_type(&chunk,riff_RIFF,riff_fourcc_const('A','V','I','X'))); if (w->enable_stream_writing) { assert(riff_stack_enable_placeholder(w->riff,&chunk)); chunk.disable_sync = 1; } assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */ if (w->enable_stream_writing) riff_stack_header_sync(w->riff,riff_stack_top(w->riff)); /* start the movi chunk */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_list_type(&chunk,riff_LIST,riff_fourcc_const('m','o','v','i'))); if (w->enable_stream_writing) { assert(riff_stack_enable_placeholder(w->riff,&chunk)); chunk.disable_sync = 1; } assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */ if (w->enable_stream_writing) riff_stack_header_sync(w->riff,riff_stack_top(w->riff)); w->movi = chunk; w->group++; } } else { /* else, if we're about to pass 2GB, then stop allowing any more data, because the traditional * AVI format uses 32-bit integers and most implementations treat them as signed. */ if (((unsigned long long)w->movi.absolute_data_offset + (unsigned long long)w->riff->top->write_offset + (unsigned long long)len) >= 0x7FF00000ULL) /* 2GB - 16MB */ return 0; } /* write chunk into movi */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_data_type(&chunk,s->chunk_fourcc)); assert(riff_stack_push(w->riff,&chunk)); if (w->enable_stream_writing) { /* use an optimized version of riff_stack_write() that blasts the RIFF chunk header + data in one go */ if (data != NULL && len > 0) assert((int)riff_stack_streamwrite(w->riff,riff_stack_top(w->riff),data,(size_t)len) == (int)len); else assert((int)riff_stack_streamwrite(w->riff,riff_stack_top(w->riff),NULL,(size_t)0) == (int)0); } else { if (data != NULL && len > 0) assert((int)riff_stack_write(w->riff,riff_stack_top(w->riff),data,(size_t)len) == (int)len); } riff_stack_pop(w->riff); /* put the data into the index */ if (!avi_writer_stream_check_samplecount(s,s->sample_write_chunk+16)) return 0; s->sample_index_max = s->sample_write_chunk+1; assert(s->sample_index_max < s->sample_index_alloc); si = s->sample_index + s->sample_write_chunk; si->stream_offset = s->sample_write_offset; si->offset = (uint64_t)chunk.absolute_data_offset; si->length = (uint32_t)len; si->dwFlags = flags; s->sample_write_offset += (unsigned int)len; s->sample_write_chunk++; /* if stream writing is not enabled, then rewrite all RIFF parent chunks to reflect the new data */ if (!w->enable_stream_writing) riff_stack_header_sync_all(w->riff); return 1; }
riff_chunk *riff_stack_pop(riff_stack *s) { riff_chunk *pc,*c; if (!s) return NULL; if (s->current == -1) return NULL; c = s->top; s->current--; if (s->current == -1) { if (s->wmode) { if (!c->disable_sync) riff_stack_header_sync(s,c); if (c->data_length < (int64_t)c->write_offset) c->data_length = (uint32_t)c->write_offset; c->absolute_data_length = (c->data_length + 1) & (~1UL); s->next_write = c->absolute_data_offset + c->absolute_data_length; } s->top = NULL; return NULL; } s->top = (s->stack + s->current); /* if we're writing a RIFF structure, we need to make sure * the parent chunk's data offsets are properly set up. * the above conditions ensure we don't get here unless * there was a parent chunk involved */ if (s->wmode) { unsigned char no_sync = c->disable_sync; if (c->placeholder) { c->placeholder = 0; /* caller pops a chunk off the stack to complete it, therefore the placeholder must be rewritten */ c->disable_sync = 0; /* it MUST be rewritten, disabling sync is no longer an option */ no_sync = 0; /* <--- ditto */ } if (!no_sync) riff_stack_header_sync(s,c); pc = s->top; while (pc && c) { if ((c->absolute_data_offset + c->absolute_data_length) > (pc->absolute_data_offset + pc->data_length)) { pc->data_length = (uint32_t)((c->absolute_data_offset + c->absolute_data_length) - pc->absolute_data_offset); pc->absolute_data_length = pc->data_length; /* FIXME: right? */ } if (c->absolute_data_offset >= pc->absolute_data_offset) { int64_t limit = c->absolute_data_offset + c->absolute_data_length - pc->absolute_data_offset; if (pc->write_offset < limit) pc->write_offset = limit; } if (pc == s->stack) break; c = pc--; /* NTS: remember pc = s->top = s->stack + <index> */ } if (!no_sync) riff_stack_header_sync(s,s->top); } return s->top; }
int avi_writer_stream_repeat_last_chunk(avi_writer *w,avi_writer_stream *s) { avi_writer_stream_index *si,*psi; riff_chunk chunk; if (w == NULL || s == NULL) return 0; if (w->state != AVI_WRITER_STATE_BODY) return 0; if (s->sample_write_chunk == 0) /* if there *IS* no previous chunk, then bail */ return 0; /* make sure we're down into the 'movi' chunk */ while (w->riff->current > 1) riff_stack_pop(w->riff); /* make sure this is the movi chunk */ if (w->riff->current != 1) return 0; if (w->riff->top->fourcc != avi_riff_movi) return 0; if (w->enable_opendml) { /* if we're writing an OpenDML 2.0 compliant file, and we're approaching a movi size of 1GB, * then split the movi chunk and start another RIFF:AVIX */ if ((unsigned long long)(w->riff->top->write_offset + 8) >= 0x3FF00000ULL) { /* 1GB - 16MB */ riff_stack_writing_sync(w->riff); /* sync all headers and pop all chunks */ assert(w->riff->current == -1); /* should be at top level */ /* at the first 1GB boundary emit AVIOLDINDEX for older AVI applications */ if (w->group == 0 && w->enable_avioldindex) avi_writer_emit_avioldindex(w); /* [1] RIFF:AVIX */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_list_type(&chunk,riff_RIFF,riff_fourcc_const('A','V','I','X'))); if (w->enable_stream_writing) { assert(riff_stack_enable_placeholder(w->riff,&chunk)); chunk.disable_sync = 1; } assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */ if (w->enable_stream_writing) riff_stack_header_sync(w->riff,riff_stack_top(w->riff)); /* start the movi chunk */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_list_type(&chunk,riff_LIST,riff_fourcc_const('m','o','v','i'))); if (w->enable_stream_writing) { assert(riff_stack_enable_placeholder(w->riff,&chunk)); chunk.disable_sync = 1; } assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */ if (w->enable_stream_writing) riff_stack_header_sync(w->riff,riff_stack_top(w->riff)); w->movi = chunk; w->group++; } } else { /* else, if we're about to pass 2GB, then stop allowing any more data, because the traditional * AVI format uses 32-bit integers and most implementations treat them as signed. */ if ((w->movi.absolute_data_offset + w->riff->top->write_offset + 8) >= 0x7FF00000LL) /* 2GB - 16MB */ return 0; } /* write chunk into movi (for consistent timekeeping with older AVI apps that don't read the index) */ assert(riff_stack_begin_new_chunk_here(w->riff,&chunk)); assert(riff_stack_set_chunk_data_type(&chunk,s->chunk_fourcc)); assert(riff_stack_push(w->riff,&chunk)); riff_stack_pop(w->riff); /* put the data into the index */ if (!avi_writer_stream_check_samplecount(s,s->sample_write_chunk+16)) return 0; /* lookup the previous chunk */ /* NTS: this must come after the check_samplecount() because check_samplecount() * uses realloc() to extend the array and realloc() may move the data around * to fullfill the request */ assert(s->sample_index != NULL); assert(s->sample_index_max >= s->sample_write_chunk); psi = s->sample_index + s->sample_write_chunk - 1; s->sample_index_max = s->sample_write_chunk+1; assert(s->sample_index_max < s->sample_index_alloc); si = s->sample_index + s->sample_write_chunk; *si = *psi; si->stream_offset = s->sample_write_offset; s->sample_write_offset += si->length; s->sample_write_chunk++; riff_stack_header_sync_all(w->riff); return 1; }