GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter)
{
	u32 i, tracksDone;
	TrackWriter *tmp, *curWriter;
	GF_Err e;
	u32 descIndex, sampSize, chunkNumber;
	u64 DTS;
	u32 moov_timescale;
	u16 curGroupID;
	Bool forceNewChunk, writeGroup;
	GF_StscEntry *stsc_ent;
	//this is used to emulate the write ...
	u64 offset, sampOffset, size, mdatSize;
	u32 count;
	GF_ISOFile *movie = mw->movie;

	mdatSize = 0;

#ifdef TEST_LARGE_FILES
	if (!Emulation) {
		char *blank;
		u32 count, i;
		i = count = 0;
		blank = gf_malloc(sizeof(char)*1024*1024);
		memset(blank, 0, sizeof(char)*1024*1024);
		count = 4096;
		memset(blank, 0, sizeof(char)*1024*1024);
		while (i<count) {
			u32 res = gf_bs_write_data(bs, blank, 1024*1024);
			if (res != 1024*1024) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("error writing to disk: only %d bytes written\n", res));
			}
			i++;
			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("writing blank block: %.02f done - %d/%d \r", (100.0*i)/count , i, count));
		}
		gf_free(blank);
	}
	mdatSize = 4096*1024;
	mdatSize *= 1024;
#endif

	/*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/
	if (movie->meta) {
		e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size);
		if (e) return e;
		mdatSize += size;
		StartOffset += (u32) size;
	}
	if (movie->moov) {
		if (movie->moov->meta) {
			e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size);
			if (e) return e;
			mdatSize += size;
			StartOffset += (u32) size;
		}
		i=0;
		while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {
			if (tmp->mdia->mediaTrack->meta) {
				e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size);
				if (e) return e;
				mdatSize += size;
				StartOffset += (u32) size;
			}
		}
	}

	if (movie->storageMode == GF_ISOM_STORE_TIGHT)
		return DoFullInterleave(mw, writers, bs, Emulation, StartOffset);

	curGroupID = 1;
	//we emulate a write from this offset...
	offset = StartOffset;
	tracksDone = 0;

#ifdef TEST_LARGE_FILES
	offset += mdatSize;
#endif

	moov_timescale = movie->moov && movie->moov->mvhd ? movie->moov->mvhd->timeScale : 1000;

	count = gf_list_count(writers);
	//browse each groups
	while (1) {
		/*the max DTS the chunk of the current writer*/
		u64 chunkLastDTS = 0;
		/*the timescale related to the max DTS*/
		u32 chunkLastScale = 0;

		writeGroup = 1;

		//proceed a group
		while (writeGroup) {
			curWriter = NULL;
			for (i=0 ; i < count; i++) {
				tmp = (TrackWriter*)gf_list_get(writers, i);

				//is it done writing ?
				if (tmp->isDone) continue;

				//is it in our group ??
				if (tmp->stbl->groupID != curGroupID) continue;

				//write till this chunk is full on this track...
				while (1) {
					Bool self_contained;
					u32 nb_samp = 1;
					u32 sample_dur;
					u64 chunk_prev_dur;
					//To Check: are empty sample tables allowed ???
					if (tmp->sampleNumber > tmp->stbl->SampleSize->sampleCount) {
						tmp->isDone = 1;
						tracksDone ++;
						break;
					}

					//OK, get the current sample in this track
					stbl_GetSampleDTS_and_Duration(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS, &sample_dur);

					//can this sample fit in our chunk ?
					if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * moov_timescale > movie->interleavingTime * tmp->timeScale
					        /*drift check: reject sample if outside our check window*/
					        || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) )
					   ) {
						//in case the sample is longer than InterleaveTime
						if (!tmp->chunkDur) {
							forceNewChunk = 1;
						} else {
							//this one is full. go to next one (exit the loop)
							tmp->chunkDur = 0;
							//forceNewChunk = 0;
							break;
						}
					} else {
						forceNewChunk = tmp->chunkDur ? 0 : 1;
					}
					//OK, we can write this track
					curWriter = tmp;

					//small check for first 2 samples (DTS = 0)
					//only in the old mode can chunkdur be 0 for dts 0
					if (tmp->sampleNumber == 2 && !tmp->chunkDur && gf_sys_old_arch_compat() ) {
						forceNewChunk = 0;
					}

					chunk_prev_dur = tmp->chunkDur;
					//FIXME we do not apply patch in test mode for now since this breaks all our hashes, remove this
					//once we move to filters permanently
					if (!gf_sys_old_arch_compat()) {
						tmp->chunkDur += sample_dur;
					} else {
						//old style, compute based on DTS diff
						tmp->chunkDur += (u32) (DTS - tmp->DTSprev);
					}
					tmp->DTSprev = DTS;

					e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
					if (e)
						return e;
					e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
					if (e)
						return e;

					self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex)) ? GF_TRUE : GF_FALSE;

					update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);

					if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
						curWriter->chunkSize = 0;
						tmp->chunkDur -= chunk_prev_dur;
						forceNewChunk = 1;
					}
					curWriter->chunkSize += sampSize;

					//do we actually write, or do we emulate ?
					if (Emulation) {
						//update our offsets...
						if (self_contained) {
							e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
							if (e)
								return e;
							offset += sampSize;
							mdatSize += sampSize;
						} else {
							if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
							curWriter->prev_offset = sampOffset + sampSize;

							//we have a DataRef, so use the offset idicated in sampleToChunk
							//and ChunkOffset tables...
							e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk, nb_samp);
							if (e) return e;
						}
					} else {
						//we're writing ....
						if (self_contained) {
							e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, nb_samp);
							if (e)
								return e;
						}
					}
					//ok, the sample is done
					if (curWriter->sampleNumber >= curWriter->stbl->SampleSize->sampleCount) {
						curWriter->isDone = 1;
						//one more track done...
						tracksDone ++;
						break;
					} else {
						curWriter->sampleNumber += nb_samp;
					}
				}
				/*record chunk end-time & track timescale for drift-controled interleaving*/
				if (drift_inter && curWriter) {
					chunkLastScale = curWriter->timeScale;
					chunkLastDTS = curWriter->DTSprev;
					/*add one interleave window drift - since the "maxDTS" is the previously written one, we will
					have the following cases:
					- sample doesn't fit: post-pone and force new chunk
					- sample time larger than previous chunk time + interleave: post-pone and force new chunk
					- otherwise store and track becomes current reference

					this ensures a proper drift regulation (max DTS diff is less than the interleaving window)
					*/
					chunkLastDTS += curWriter->timeScale * movie->interleavingTime / moov_timescale;
				}
			}
			//no sample found, we're done with this group
			if (!curWriter) {
				writeGroup = 0;
				continue;
			}
		}
		//if all our track are done, break
		if (tracksDone == gf_list_count(writers)) break;
		//go to next group
		curGroupID ++;
	}
	if (movie->mdat) movie->mdat->dataSize = mdatSize;
	return GF_OK;
}

fixed #1659


static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset)
{
	u32 i, j, k, l, last;
	TrackWriter *writer;
	GF_StscEntry *ent;

	if (file->meta) ShiftMetaOffset(file->meta, offset);
	if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset);

	i=0;
	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
		if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset);

		//we have to proceed entry by entry in case a part of the media is not self-contained...
		for (j=0; j<writer->stsc->nb_entries; j++) {
			ent = &writer->stsc->entries[j];
			if ((writer->all_dref_mode==ISOM_DREF_EXT) || !Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex))
				continue;

			//OK, get the chunk(s) number(s) and "shift" its (their) offset(s).
			if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
				GF_ChunkLargeOffsetBox *new_stco64 = NULL;
				GF_ChunkOffsetBox *stco = (GF_ChunkOffsetBox *) writer->stco;

				//be carefull for the last entry, nextChunk is set to 0 in edit mode...
				last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1;
				for (k = ent->firstChunk; k < last; k++) {

					//we need to rewrite the table: only allocate co64 if not done previously and convert all offsets
					//to co64. Then (whether co64 was created or not) adjust the offset
					//Do not reassign table until we are done with the current sampleToChunk processing
					//since we have a test on stco->offsets[k-1], we need to keep stco untouched
					if (new_stco64 || file->force_co64 || (stco->offsets[k-1] + offset > 0xFFFFFFFF)) {
						if (!new_stco64) {
							new_stco64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
							if (!new_stco64) return GF_OUT_OF_MEM;
							new_stco64->nb_entries = stco->nb_entries;
							new_stco64->offsets = (u64 *) gf_malloc(new_stco64->nb_entries * sizeof(u64));
							if (!new_stco64->offsets) return GF_OUT_OF_MEM;
							//copy over the stco table
							for (l = 0; l < new_stco64->nb_entries; l++) {
								new_stco64->offsets[l] = (u64) stco->offsets[l];
							}
						}
						new_stco64->offsets[k-1] += offset;
					} else {
						stco->offsets[k-1] += (u32) offset;
					}
				}
				if (new_stco64) {
					//done with this sampleToChunk entry, replace the box if we moved to co64
					gf_isom_box_del(writer->stco);
					writer->stco = (GF_Box *)new_stco64;
					new_stco64 = NULL;
				}
			} else {
				GF_ChunkLargeOffsetBox *stco64 = (GF_ChunkLargeOffsetBox *) writer->stco;
				//be carefull for the last entry ...
				last = ent->nextChunk ? ent->nextChunk : stco64->nb_entries + 1;
				for (k = ent->firstChunk; k < last; k++) {
					stco64->offsets[k-1] += offset;
				}
			}
		}
	}

	return GF_OK;

}

fixed #1659


static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs)
{
	u32 i;
	TrackWriter *writer;
	GF_Err e;
	GF_Box *stco;
	GF_SampleToChunkBox *stsc;

	if (movie->meta) {
		//write the moov box...
		e = gf_isom_box_size((GF_Box *)movie->meta);
		if (e) return e;
		e = gf_isom_box_write((GF_Box *)movie->meta, bs);
		if (e) return e;
	}

	if (movie->moov) {
		Bool prevent_dispatch = GF_FALSE;
		//switch all our tables
		i=0;
		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
			//don't delete them !!!
			stsc = writer->stbl->SampleToChunk;
			stco = writer->stbl->ChunkOffset;
			s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, stsc);
			s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, stco);
			writer->stbl->SampleToChunk = writer->stsc;
			writer->stbl->ChunkOffset = writer->stco;
			gf_list_insert(writer->stbl->child_boxes, writer->stsc, stsc_pos);
			gf_list_insert(writer->stbl->child_boxes, writer->stco, stco_pos);
			writer->stco = stco;
			writer->stsc = stsc;
			if (writer->prevent_dispatch)
				prevent_dispatch = GF_TRUE;
		}
		if (prevent_dispatch) {
			gf_bs_prevent_dispatch(bs, GF_TRUE);
		}
		//write the moov box...
		e = gf_isom_box_size((GF_Box *)movie->moov);
		if (e) return e;

		if ((movie->compress_mode==GF_ISO_COMP_ALL) || (movie->compress_mode==GF_ISO_COMP_MOOV)) {
			e = gf_isom_write_compressed_box(movie, (GF_Box *) movie->moov, GF_4CC('!', 'm', 'o', 'v'), bs, NULL);
		} else {
			e = gf_isom_box_write((GF_Box *)movie->moov, bs);
		}

		if (prevent_dispatch) {
			gf_bs_prevent_dispatch(bs, GF_FALSE);
		}

		//and re-switch our table. We have to do it that way because it is
		//needed when the moov is written first
		i=0;
		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
			//don't delete them !!!
			stsc = writer->stsc;
			stco = writer->stco;
			writer->stsc = writer->stbl->SampleToChunk;
			writer->stco = writer->stbl->ChunkOffset;
			s32 stsc_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stsc);
			s32 stco_pos = gf_list_del_item(writer->stbl->child_boxes, writer->stco);

			writer->stbl->SampleToChunk = stsc;
			writer->stbl->ChunkOffset = stco;
			gf_list_insert(writer->stbl->child_boxes, stsc, stsc_pos);
			gf_list_insert(writer->stbl->child_boxes, stco, stco_pos);
		}
		if (e) return e;
	}
	return GF_OK;
}

fixed #1659


static GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs, Bool non_seakable, Bool for_fragments, GF_BitStream *moov_bs)
{
	GF_Err e;
	u32 i;
	u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
	GF_Box *a, *cprt_box=NULL;
	GF_List *writers = gf_list_new();
	GF_ISOFile *movie = mw->movie;
	s32 moov_meta_pos=-1;

	//in case we did a read on the file while producing it, seek to end of edit
	totSize = gf_bs_get_size(bs);
	if (gf_bs_get_position(bs) != totSize) {
		gf_bs_seek(bs, totSize);
	}
	begin = totSize = 0;

	//first setup the writers
	e = SetupWriters(mw, writers, 0);
	if (e) goto exit;

	if (!moovFirst) {
		if ((movie->openMode == GF_ISOM_OPEN_WRITE) && !non_seakable) {
			begin = 0;
			totSize = gf_isom_datamap_get_offset(movie->editFileMap);
			/*start boxes have not been written yet, do it*/
			if (!totSize) {
				if (movie->is_jp2) {
					gf_bs_write_u32(movie->editFileMap->bs, 12);
					gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_JP);
					gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
					totSize += 12;
					begin += 12;
				}
				if (movie->brand) {
					e = gf_isom_box_size((GF_Box *)movie->brand);
					if (e) goto exit;
					e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
					if (e) goto exit;
					totSize += movie->brand->size;
					begin += movie->brand->size;
				}
				if (movie->pdin) {
					e = gf_isom_box_size((GF_Box *)movie->pdin);
					if (e) goto exit;
					e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
					if (e) goto exit;
					totSize += movie->pdin->size;
					begin += movie->pdin->size;
				}
			} else {
				if (movie->is_jp2) begin += 12;
				if (movie->brand) begin += movie->brand->size;
				if (movie->pdin) begin += movie->pdin->size;
			}
			totSize -= begin;
		} else if (!non_seakable || for_fragments) {
			if (movie->is_jp2) {
				gf_bs_write_u32(bs, 12);
				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
				gf_bs_write_u32(bs, 0x0D0A870A);
			}
			if (movie->brand) {
				e = gf_isom_box_size((GF_Box *)movie->brand);
				if (e) goto exit;
				e = gf_isom_box_write((GF_Box *)movie->brand, bs);
				if (e) goto exit;
			}
			/*then progressive download*/
			if (movie->pdin) {
				e = gf_isom_box_size((GF_Box *)movie->pdin);
				if (e) goto exit;
				e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
				if (e) goto exit;
			}
		}

		//if the moov is at the end, write directly
		i=0;
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			switch (a->type) {
			/*written by hand*/
			case GF_ISOM_BOX_TYPE_MOOV:
			case GF_ISOM_BOX_TYPE_META:
				moov_meta_pos = i-1;
			case GF_ISOM_BOX_TYPE_FTYP:
			case GF_ISOM_BOX_TYPE_PDIN:
#ifndef GPAC_DISABLE_ISOM_ADOBE
			case GF_ISOM_BOX_TYPE_AFRA:
			case GF_ISOM_BOX_TYPE_ABST:
#endif
				break;
			case GF_ISOM_BOX_TYPE_MDAT:
				//in case we're capturing
				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
					//emulate a write to recreate our tables (media data already written)
					e = DoWrite(mw, writers, bs, 1, begin);
					if (e) goto exit;
					continue;
				}
				if (non_seakable) {
					begin = gf_bs_get_position(bs);
					//do a sim pass to get the true mdat size
					e = DoWrite(mw, writers, bs, 1, begin);
					if (e) goto exit;

					if (movie->mdat->dataSize > 0xFFFFFFFF) {
						gf_bs_write_u32(bs, 1);
					} else {
						gf_bs_write_u32(bs, (u32) movie->mdat->dataSize + 8);
					}
					gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
					if (movie->mdat->dataSize > 0xFFFFFFFF) gf_bs_write_u64(bs, movie->mdat->dataSize + 8 + 8);
					//reset writers and write samples
					ResetWriters(writers);
					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
					if (e) goto exit;
					movie->mdat->size = movie->mdat->dataSize;
					totSize = 0;
				} else {
					//to avoid computing the size each time write always 4 + 4 + 8 bytes before
					begin = gf_bs_get_position(bs);
					gf_bs_write_u64(bs, 0);
					gf_bs_write_u64(bs, 0);
					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
					if (e) goto exit;
					totSize = gf_bs_get_position(bs) - begin;
				}
				break;

			case GF_ISOM_BOX_TYPE_FREE:
				//for backward compat with old arch, keep copyright before moov
				if (((GF_FreeSpaceBox*)a)->dataSize>4) {
					GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
					if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
						e = gf_isom_box_size(a);
						if (e) goto exit;
						e = gf_isom_box_write(a, bs);
						if (e) goto exit;
						cprt_box = a;
						break;
					}
				}
			default:
				if (moov_meta_pos < 0) {
					e = gf_isom_box_size(a);
					if (e) goto exit;
					e = gf_isom_box_write(a, bs);
					if (e) goto exit;
				}
				break;
			}
		}

		if (moov_bs) {
			e = DoWrite(mw, writers, bs, 1, movie->mdat->bsOffset);
			if (e) goto exit;

			firstSize = GetMoovAndMetaSize(movie, writers);

			offset = firstSize;
			e = ShiftOffset(movie, writers, offset);
			if (e) goto exit;
			//get the size and see if it has changed (eg, we moved to 64 bit offsets)
			finalSize = GetMoovAndMetaSize(movie, writers);
			if (firstSize != finalSize) {
				finalOffset = finalSize;
				//OK, now we're sure about the final size.
				//we don't need to re-emulate, as the only thing that changed is the offset
				//so just shift the offset
				e = ShiftOffset(movie, writers, finalOffset - offset);
				if (e) goto exit;
			}
		}
		//get real sample offsets for meta items
		if (movie->meta) {
			store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
		}
		//OK, write the movie box.
		e = WriteMoovAndMeta(movie, writers, moov_bs ? moov_bs : bs);
		if (e) goto exit;

#ifndef GPAC_DISABLE_ISOM_ADOBE
		i=0;
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			switch (a->type) {
			case GF_ISOM_BOX_TYPE_AFRA:
			case GF_ISOM_BOX_TYPE_ABST:
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
				break;
			}
		}
#endif

		/*if data has been written, update mdat size*/
		if (totSize) {
			offset = gf_bs_get_position(bs);
			e = gf_bs_seek(bs, begin);
			if (e) goto exit;
			if (totSize > 0xFFFFFFFF) {
				gf_bs_write_u32(bs, 1);
			} else {
				gf_bs_write_u32(bs, (u32) totSize);
			}
			gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
			if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
			e = gf_bs_seek(bs, offset);
			movie->mdat->size = totSize;
		}

		//then the rest
		i = (u32) (moov_meta_pos + 1);
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			if (a==cprt_box) continue;

			switch (a->type) {
			case GF_ISOM_BOX_TYPE_MOOV:
			case GF_ISOM_BOX_TYPE_META:
			case GF_ISOM_BOX_TYPE_FTYP:
			case GF_ISOM_BOX_TYPE_PDIN:
			case GF_ISOM_BOX_TYPE_MDAT:
				break;
			default:
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
		}
		goto exit;
	}

	//nope, we have to write the moov first. The pb is that
	//1 - we don't know its size till the mdat is written
	//2 - we don't know the ofset at which the mdat will start...
	//3 - once the mdat is written, the chunkOffset table can have changed...

	if (movie->is_jp2) {
		gf_bs_write_u32(bs, 12);
		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
		gf_bs_write_u32(bs, 0x0D0A870A);
	}
	if (movie->brand) {
		e = gf_isom_box_size((GF_Box *)movie->brand);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
		if (e) goto exit;
	}
	/*then progressive dnload*/
	if (movie->pdin) {
		e = gf_isom_box_size((GF_Box *)movie->pdin);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
		if (e) goto exit;
	}

	//write all boxes before moov
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
			moov_meta_pos = i-1;
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		//for backward compat with old arch keep out copyright after moov
		case GF_ISOM_BOX_TYPE_FREE:
			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
					cprt_box = a;
					break;
				}
			}
		default:
			if (moov_meta_pos<0) {
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
			break;
		}
	}

	//What we will do is first emulate the write from the beginning...
	//note: this will set the size of the mdat
	e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
	if (e) goto exit;

	firstSize = GetMoovAndMetaSize(movie, writers);
	//offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	e = ShiftOffset(movie, writers, offset);
	if (e) goto exit;
	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
	finalSize = GetMoovAndMetaSize(movie, writers);
	if (firstSize != finalSize) {
		finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
		//OK, now we're sure about the final size.
		//we don't need to re-emulate, as the only thing that changed is the offset
		//so just shift the offset
		e = ShiftOffset(movie, writers, finalOffset - offset);
		if (e) goto exit;
	}
	//now write our stuff
	e = WriteMoovAndMeta(movie, writers, bs);
	if (e) goto exit;
	e = gf_isom_box_size((GF_Box *)movie->mdat);
	if (e) goto exit;
	e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
	if (e) goto exit;

	//we don't need the offset as the moov is already written...
	ResetWriters(writers);
	e = DoWrite(mw, writers, bs, 0, 0);
	if (e) goto exit;

	//then the rest
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		if ((i-1<= (u32) moov_meta_pos) && (a!=cprt_box)) continue;
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		default:
			e = gf_isom_box_size(a);
			if (e) goto exit;
			e = gf_isom_box_write(a, bs);
			if (e) goto exit;
		}
	}

exit:
	CleanWriters(writers);
	gf_list_del(writers);
	return e;
}

fixed #1659


GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs, u32 nb_samp)
{
	GF_DataMap *map;
	u32 bytes;

	if (!size) return GF_OK;

	if (size>mw->alloc_size) {
		mw->buffer = (char*)gf_realloc(mw->buffer, size);
		mw->alloc_size = size;
	}

	if (!mw->buffer) return GF_OUT_OF_MEM;

	if (isEdited) {
		map = mw->movie->editFileMap;
	} else {
		map = mw->movie->movieFileMap;
	}
	//get the payload...
	bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
	if (bytes != size)
		return GF_IO_ERR;
	//write it to our stream...
	bytes = gf_bs_write_data(bs, mw->buffer, size);
	if (bytes != size)
		return GF_IO_ERR;

	mw->nb_done+=nb_samp;
	muxer_report_progress(mw);
	return GF_OK;
}

fixed #1659


GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize)
{
#ifdef GPAC_DISABLE_ZLIB
	return GF_NOT_SUPPORTED;
#else
	GF_Err e;
	GF_BitStream *comp_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
	e = gf_isom_box_write(root_box, comp_bs);

	if (!e) {
		u8 *box_data;
		u32 box_size, comp_size;

		if (box_csize)
			*box_csize = (u32) root_box->size;

		gf_bs_get_content(comp_bs, &box_data, &box_size);
		gf_gz_compress_payload_ex(&box_data, box_size, &comp_size, 8, GF_TRUE, NULL);
		if (mov->force_compress || (comp_size + COMP_BOX_COST_BYTES < box_size)) {
			if (bs) {
				gf_bs_write_u32(bs, comp_size+8);
				gf_bs_write_u32(bs, repl_type);
				gf_bs_write_data(bs, box_data, comp_size);
			}
			if (box_csize)
				*box_csize = comp_size + COMP_BOX_COST_BYTES;
		} else if (bs) {
			gf_bs_write_data(bs, box_data, box_size);
		}
		gf_free(box_data);
	}
	gf_bs_del(comp_bs);
	return e;
#endif /*GPAC_DISABLE_ZLIB*/
}

fixed #1659


static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
{
	GF_Err e;
	u32 i;
	s32 moov_meta_pos=-1;
	GF_Box *a, *cprt_box=NULL;
	u64 firstSize, finalSize, offset, finalOffset;
	GF_List *writers = gf_list_new();
	GF_ISOFile *movie = mw->movie;

	//first setup the writers
	e = SetupWriters(mw, writers, 1);
	if (e) goto exit;

	if (movie->is_jp2) {
		gf_bs_write_u32(bs, 12);
		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
		gf_bs_write_u32(bs, 0x0D0A870A);
	}
	if (movie->brand) {
		e = gf_isom_box_size((GF_Box *)movie->brand);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
		if (e) goto exit;
	}
	if (movie->pdin) {
		e = gf_isom_box_size((GF_Box *)movie->pdin);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
		if (e) goto exit;
	}

	//write all boxes before moov
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
			moov_meta_pos = i-1;
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		case GF_ISOM_BOX_TYPE_FREE:
			//for backward compat with old arch, keep copyright before moov
			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
					cprt_box = a;
					break;
				}
			}
		default:
			if (moov_meta_pos<0) {
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
			break;
		}
	}

	e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
	if (e) goto exit;

	firstSize = GetMoovAndMetaSize(movie, writers);
	offset = firstSize;
	if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	e = ShiftOffset(movie, writers, offset);
	if (e) goto exit;
	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
	finalSize = GetMoovAndMetaSize(movie, writers);
	if (firstSize != finalSize) {
		finalOffset = finalSize;
		if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
		//OK, now we're sure about the final size -> shift the offsets
		//we don't need to re-emulate, as the only thing that changed is the offset
		//so just shift the offset
		e = ShiftOffset(movie, writers, finalOffset - offset);
		if (e) goto exit;
		/*firstSize = */GetMoovAndMetaSize(movie, writers);
	}
	//get real sample offsets for meta items
	if (movie->meta) {
		store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
	}
	//now write our stuff
	e = WriteMoovAndMeta(movie, writers, bs);
	if (e) goto exit;

	/*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
	if (movie->mdat && movie->mdat->dataSize) {
		if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
		e = gf_isom_box_size((GF_Box *)movie->mdat);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
		if (e) goto exit;
	}

	//we don't need the offset as we are writing...
	ResetWriters(writers);
	e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
	if (e) goto exit;

	//then the rest
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		if ((i-1 < (u32) moov_meta_pos) && (a != cprt_box))
			continue;
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		default:
			e = gf_isom_box_size(a);
			if (e) goto exit;
			e = gf_isom_box_write(a, bs);
			if (e) goto exit;
		}
	}

exit:
	CleanWriters(writers);
	gf_list_del(writers);
	return e;
}

fixed #1659


GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
{
	GF_ItemExtentEntry *entry;
	u64 maxExtendOffset, maxExtendSize;
	u32 i, j, count;

	maxExtendOffset = 0;
	maxExtendSize = 0;
	if (mdatSize) *mdatSize = 0;
	if (!meta->item_locations) return GF_OK;

	count = gf_list_count(meta->item_locations->location_entries);
	for (i=0; i<count; i++) {
		u64 it_size;
		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
		/*get item info*/
		GF_ItemInfoEntryBox *iinf = NULL;
		j=0;
		while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
			if (iinf->item_ID==iloc->item_ID) break;
			iinf = NULL;
		}

		if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
				entry->extent_offset = 0;
				continue;
			}
		}

		it_size = 0;
		/*for self contained only*/
		if (!iloc->data_reference_index) {
			if (iloc->construction_method != 2) {
				iloc->base_offset = baseOffset;
			}

			/*new resource*/
			if (iinf && (iinf->full_path || (iinf->tk_id && iinf->sample_num))) {
				FILE *src=NULL;

				if (!iinf->data_len && iinf->full_path) {
					src = gf_fopen(iinf->full_path, "rb");
					if (!src) continue;
					it_size = gf_fsize(src);
				} else {
					it_size = iinf->data_len;
				}
				if (maxExtendSize<it_size) maxExtendSize = it_size;

				if (!gf_list_count(iloc->extent_entries)) {
					GF_SAFEALLOC(entry, GF_ItemExtentEntry);
					if (!entry) return GF_OUT_OF_MEM;
					gf_list_add(iloc->extent_entries, entry);
				}
				entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
				entry->extent_offset = 0;
				entry->extent_length = it_size;

				//shared data, do not count it
				if (iinf->tk_id && iinf->sample_num) {
					it_size = 0;
					maxExtendOffset = 0xFFFFFFFFFFUL;
					if (Emulation) {
						meta->use_item_sample_sharing = GF_TRUE;
					}
				}

				/*OK write to mdat*/
				if (!Emulation) {
					if (iinf->tk_id && iinf->sample_num) {
					}
					else if (src) {
						char cache_data[4096];
						u64 remain = entry->extent_length;
						while (remain) {
							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
							size_t read = gf_fread(cache_data, size_cache, src);
							if (read ==(size_t) -1) break;
							gf_bs_write_data(bs, cache_data, (u32) read);
							remain -= (u32) read;
						}
					} else {
						gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
					}
				}
				if (src) gf_fclose(src);
			}
			else if (gf_list_count(iloc->extent_entries)) {
				j=0;
				while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
					if (entry->extent_index) continue;
					if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
					/*compute new offset*/
					entry->extent_offset = baseOffset + it_size;

					it_size += entry->extent_length;
					if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;

					/*Reading from the input file*/
					if (!Emulation) {
						char cache_data[4096];
						u64 remain = entry->extent_length;
						gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
						while (remain) {
							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
							gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
							/*Writing to the output file*/
							gf_bs_write_data(bs, cache_data, size_cache);
							remain -= size_cache;
						}
					}
				}
			}
			baseOffset += it_size;
			if (mdatSize)
				*mdatSize += it_size;
		} else {
			/*we MUST have at least one extent for the dref data*/
			if (!gf_list_count(iloc->extent_entries)) {
				GF_SAFEALLOC(entry, GF_ItemExtentEntry);
				if (!entry) return GF_OUT_OF_MEM;
				gf_list_add(iloc->extent_entries, entry);
			}
			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			entry->extent_offset = 0;
			/*0 means full length of referenced file*/
			entry->extent_length = 0;
		}
	}

	/*update offset & size length fields*/
	if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
	else if (baseOffset) meta->item_locations->base_offset_size = 4;

	if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
	else if (maxExtendSize) meta->item_locations->length_size = 4;

	if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
	else if (maxExtendOffset) meta->item_locations->offset_size = 4;
	return GF_OK;
}

fixed #1659


GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{

	u32 i, tracksDone;
	TrackWriter *tmp, *curWriter, *prevWriter;
	GF_Err e;
	u64 DTS, DTStmp, TStmp;
	s64 res;
	u32 descIndex, sampSize, chunkNumber;
	u16 curGroupID, curTrackPriority;
	Bool forceNewChunk, writeGroup;
	GF_StscEntry *stsc_ent;
	//this is used to emulate the write ...
	u64 offset, totSize, sampOffset;
	GF_ISOFile *movie = mw->movie;

	totSize = 0;
	curGroupID = 1;

	prevWriter = NULL;
	//we emulate a write from this offset...
	offset = StartOffset;
	tracksDone = 0;

	//browse each groups
	while (1) {
		writeGroup = 1;

		//proceed a group
		while (writeGroup) {
			u32 nb_samp = 1;
			Bool self_contained, chunked_forced=GF_FALSE;
			//first get the appropriated sample for the min time in this group
			curWriter = NULL;
			DTStmp = (u64) -1;
			TStmp = 0;
			curTrackPriority = (u16) -1;

			i=0;
			while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {

				//is it done writing ?
				//is it in our group ??
				if (tmp->isDone || tmp->stbl->groupID != curGroupID) continue;

				//OK, get the current sample in this track
				stbl_GetSampleDTS(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS);
				res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
				if (res < 0) continue;
				if ((!res) && curTrackPriority <= tmp->stbl->trackPriority) continue;
				curWriter = tmp;
				curTrackPriority = tmp->stbl->trackPriority;
				DTStmp = DTS;
				TStmp = tmp->timeScale;
			}
			//no sample found, we're done with this group
			if (!curWriter) {
				//we're done with the group
				writeGroup = 0;
				continue;
			}
			//To Check: are empty sample tables allowed ???
			if (curWriter->sampleNumber > curWriter->stbl->SampleSize->sampleCount) {
				curWriter->isDone = 1;
				tracksDone ++;
				continue;
			}

			e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
			if (e) return e;
			e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
			if (e) return e;

			update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);

			if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
				curWriter->chunkSize = 0;
				chunked_forced = forceNewChunk = 1;
			}
			curWriter->chunkSize += sampSize;

			self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;

			//do we actually write, or do we emulate ?
			if (Emulation) {
				//are we in the same track ??? If not, force a new chunk when adding this sample
				if (!chunked_forced) {
					if (curWriter != prevWriter) {
						forceNewChunk = 1;
					} else {
						forceNewChunk = 0;
					}
				}
				//update our offsets...
				if (self_contained) {
					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
					if (e) return e;
					offset += sampSize;
					totSize += sampSize;
				} else {
//					if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
					curWriter->prev_offset = sampOffset + sampSize;

					//we have a DataRef, so use the offset idicated in sampleToChunk
					//and ChunkOffset tables...
					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, chunked_forced, nb_samp);
					if (e) return e;
				}
			} else {
				//this is no game, we're writing ....
				if (self_contained) {
					e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
					if (e) return e;
				}
			}
			//ok, the sample is done
			if (curWriter->sampleNumber == curWriter->stbl->SampleSize->sampleCount) {
				curWriter->isDone = 1;
				//one more track done...
				tracksDone ++;
			} else {
				curWriter->sampleNumber += nb_samp;
			}
			prevWriter = curWriter;
		}
		//if all our track are done, break
		if (tracksDone == gf_list_count(writers)) break;
		//go to next group
		curGroupID ++;
	}
	if (movie->mdat)
		movie->mdat->dataSize = totSize;
	return GF_OK;
}

fixed #1659


static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
{
	u32 i, count;
	if (!meta->item_locations) return;

	count = gf_list_count(meta->item_locations->location_entries);
	for (i=0; i<count; i++) {
		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
		if (iloc->data_reference_index) continue;
		if (iloc->construction_method == 2) continue;
		if (!iloc->base_offset) {
			GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
				continue;
		}

		iloc->base_offset += offset;
	}
}

fixed #1659


u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
{
	u32 i;
	u64 size;

	size = 0;
	if (movie->moov) {
		TrackWriter *writer;
		gf_isom_box_size((GF_Box *)movie->moov);
		size = movie->moov->size;
		if (size > 0xFFFFFFFF) size += 8;

		i=0;
		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
			size -= writer->stbl->ChunkOffset->size;
			size -= writer->stbl->SampleToChunk->size;
			gf_isom_box_size((GF_Box *)writer->stsc);
			gf_isom_box_size(writer->stco);
			size += writer->stsc->size;
			size += writer->stco->size;
		}
	}
	if (movie->meta) {
		u64 msize;
		gf_isom_box_size((GF_Box *)movie->meta);
		msize = movie->meta->size;
		if (msize > 0xFFFFFFFF) msize += 8;
		size += msize;
	}
	return size;
}

fixed #1659


GF_Err WriteToFile(GF_ISOFile *movie, Bool for_fragments)
{
	MovieWriter mw;
	GF_Err e = GF_OK;
	if (!movie) return GF_BAD_PARAM;

	if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;

	e = gf_isom_insert_copyright(movie);
	if (e) return e;

	memset(&mw, 0, sizeof(mw));
	mw.movie = movie;

	if (movie->moov) {
		u32 i;
		GF_TrackBox *trak;
		if (gf_sys_is_test_mode()) {
			movie->moov->mvhd->creationTime = 0;
			movie->moov->mvhd->modificationTime = 0;
		}
		i=0;
		while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
			if (gf_sys_is_test_mode()) {
				trak->Header->creationTime = 0;
				trak->Header->modificationTime = 0;
				if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
					gf_free(trak->Media->handler->nameUTF8);
					trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
				}
				trak->Media->mediaHeader->creationTime = 0;
				trak->Media->mediaHeader->modificationTime = 0;
			}
			if (trak->chunk_cache) {
				gf_isom_flush_chunk(trak, GF_TRUE);
			}
		}
	}
	//capture mode: we don't need a new bitstream
	if (movie->openMode == GF_ISOM_OPEN_WRITE) {
		if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
			GF_BitStream *bs, *moov_bs=NULL;
			u64 mdat_end = gf_bs_get_position(movie->editFileMap->bs);
			u64 mdat_start = movie->mdat->bsOffset;
			u64 mdat_size = mdat_end - mdat_start;

			if (for_fragments) {
				if (!movie->on_block_out) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
					return GF_BAD_PARAM;
				}
				bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
				e = WriteFlat(&mw, 0, bs, GF_TRUE, GF_TRUE, NULL);
				movie->fragmented_file_pos = gf_bs_get_position(bs);
				gf_bs_del(bs);
				return e;
			}
			//seek at end in case we had a read of the file
			gf_bs_seek(movie->editFileMap->bs, gf_bs_get_size(movie->editFileMap->bs) );

			if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && mdat_start && mdat_size) {
				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			}
			//write as non-seekable
			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_TRUE, GF_FALSE, moov_bs);

			movie->fragmented_file_pos = gf_bs_get_position(movie->editFileMap->bs);

			if (mdat_start && mdat_size) {
				u8 data[16];
				if (!movie->on_block_out) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block patch callback, cannot patch mdat size in flat storage\n"));
					return GF_BAD_PARAM;
				}

				//create a patch packet for mdat covering out 16 bytes (cf FlushCapture)
				bs = gf_bs_new(data, 16, GF_BITSTREAM_WRITE);
				gf_bs_write_u32(bs, (mdat_size>0xFFFFFFFF) ? 1 : (u32) mdat_size);
				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
				if  (mdat_size>0xFFFFFFFF)
					gf_bs_write_u64(bs, mdat_size);
				else
					gf_bs_write_u64(bs, 0);
				gf_bs_del(bs);
				movie->on_block_patch(movie->on_block_out_usr_data, data, 16, mdat_start, GF_FALSE);
			}

			if (moov_bs) {
				u8 *moov_data;
				u32 moov_size;

				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
				gf_bs_del(moov_bs);

				movie->on_block_patch(movie->on_block_out_usr_data, moov_data, moov_size, mdat_start, GF_TRUE);
				gf_free(moov_data);
			}
		} else {
			GF_BitStream *moov_bs = NULL;
			if ((movie->storageMode==GF_ISOM_STORE_STREAMABLE) || (movie->storageMode==GF_ISOM_STORE_FASTSTART) ) {
				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			}
			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_FALSE, GF_FALSE, moov_bs);
			if (moov_bs) {
				u8 *moov_data;
				u32 moov_size;

				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
				gf_bs_del(moov_bs);
				if (!e)
					e = gf_bs_insert_data(movie->editFileMap->bs, moov_data, moov_size, movie->mdat->bsOffset);

				gf_free(moov_data);
			}
		}
	} else {
		FILE *stream=NULL;
		Bool is_stdout = GF_FALSE;
		GF_BitStream *bs=NULL;
		if (!strcmp(movie->finalName, "_gpac_isobmff_redirect")) {
			if (!movie->on_block_out) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
				return GF_BAD_PARAM;
			}
			bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
			is_stdout = GF_TRUE;
		} else {
			if (!strcmp(movie->finalName, "std"))
				is_stdout = GF_TRUE;

			//OK, we need a new bitstream
			stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
			if (!stream)
				return GF_IO_ERR;
			bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
		}
		if (!bs) {
			if (!is_stdout)
				gf_fclose(stream);
			return GF_OUT_OF_MEM;
		}

		switch (movie->storageMode) {
		case GF_ISOM_STORE_TIGHT:
		case GF_ISOM_STORE_INTERLEAVED:
			e = WriteInterleaved(&mw, bs, 0);
			break;
		case GF_ISOM_STORE_DRIFT_INTERLEAVED:
			e = WriteInterleaved(&mw, bs, 1);
			break;
		case GF_ISOM_STORE_STREAMABLE:
			e = WriteFlat(&mw, 1, bs, is_stdout, GF_FALSE, NULL);
			break;
		default:
			e = WriteFlat(&mw, 0, bs, is_stdout, GF_FALSE, NULL);
			break;
		}

		gf_bs_del(bs);
		if (!is_stdout)
			gf_fclose(stream);
	}
	if (mw.buffer) gf_free(mw.buffer);
	if (mw.nb_done<mw.total_samples) {
		mw.nb_done = mw.total_samples;
		muxer_report_progress(&mw);
	}
	return e;
}

fixed #1659


GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
{
	u32 i, trackCount;
	TrackWriter *writer;
	GF_TrackBox *trak;
	GF_ISOFile *movie = mw->movie;

	mw->total_samples = mw->nb_done = 0;
	if (!movie->moov) return GF_OK;

	trackCount = gf_list_count(movie->moov->trackList);
	for (i = 0; i < trackCount; i++) {
		GF_SampleTableBox *stbl;
		trak = gf_isom_get_track(movie->moov, i+1);

		stbl = (trak->Media && trak->Media->information) ? trak->Media->information->sampleTable : NULL;
		if (!stbl || !stbl->SampleSize || !stbl->ChunkOffset || !stbl->SampleToChunk) {
			return GF_ISOM_INVALID_FILE;
		}

		GF_SAFEALLOC(writer, TrackWriter);
		if (!writer) goto exit;
		writer->sampleNumber = 1;
		writer->mdia = trak->Media;
		writer->stbl = trak->Media->information->sampleTable;
		writer->timeScale = trak->Media->mediaHeader->timeScale;
		writer->all_dref_mode = Media_SelfContainedType(writer->mdia);

		if (trak->sample_encryption)
			writer->prevent_dispatch = GF_TRUE;

		writer->isDone = 0;
		writer->DTSprev = 0;
		writer->chunkDur = 0;
		writer->chunkSize = 0;
		writer->constant_size = writer->constant_dur = 0;
		if (writer->stbl->SampleSize->sampleSize)
			writer->constant_size = writer->stbl->SampleSize->sampleSize;
		if (writer->stbl->TimeToSample->nb_entries==1) {
			writer->constant_dur = writer->stbl->TimeToSample->entries[0].sampleDelta;
			if (writer->constant_dur>1) writer->constant_dur = 0;
		}
		if (!writer->constant_dur || !writer->constant_size || (writer->constant_size>=10))
			writer->constant_size = writer->constant_dur = 0;

		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
		if (!writer->stsc) return GF_OUT_OF_MEM;
		if (writer->stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
		} else {
			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
		}
		if (!writer->stco) return GF_OUT_OF_MEM;
		/*stops from chunk escape*/
		if (interleaving) writer->stbl->MaxSamplePerChunk = 0;
		/*for progress, assume only one descIndex*/
		if (Media_IsSelfContained(writer->mdia, 1))
			mw->total_samples += writer->stbl->SampleSize->sampleCount;
		/*optimization for interleaving: put audio last (this can be overridden by priorities)*/
		if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
			gf_list_add(writers, writer);
		} else {
			if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
				gf_list_add(writers, writer);
			} else {
				gf_list_insert(writers, writer, 0);
			}
		}
		if (movie->sample_groups_in_traf && trak->Media->information->sampleTable) {
			gf_isom_box_array_del_parent(&trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroupsDescription);
			trak->Media->information->sampleTable->sampleGroupsDescription = NULL;
		}
	}
	return GF_OK;

exit:
	CleanWriters(writers);
	return GF_OUT_OF_MEM;
}

fixed #1659


GF_Err ResetWriters(GF_List *writers)
{
	u32 i;
	TrackWriter *writer;
	i=0;
	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
		writer->isDone = 0;
		writer->chunkDur = 0;
		writer->chunkSize = 0;
		writer->DTSprev = 0;
		writer->sampleNumber = 1;
		gf_isom_box_del((GF_Box *)writer->stsc);
		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
		if (!writer->stsc) return GF_OUT_OF_MEM;
		if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
			gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
			((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
			((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
			((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
		} else {
			gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
			((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
			((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
			((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
		}
	}
	return GF_OK;
}

fixed #1659


static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
{
	u32 i;
	GF_Box *a;
	GF_FreeSpaceBox *_free;
	i=0;
	while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
		if (a->type == GF_ISOM_BOX_TYPE_FREE) {
			_free = (GF_FreeSpaceBox *)a;
			if (_free->dataSize) {
				if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
				if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
					gf_free(_free->data);
					_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
					_free->dataSize = 1 + (u32) strlen(_free->data);
					return GF_OK;
				}
			}
		}
	}
	a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
	if (!a) return GF_OUT_OF_MEM;
	_free = (GF_FreeSpaceBox *)a;
	_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
	_free->dataSize = (u32) strlen(_free->data) + 1;
	if (!_free->data) return GF_OUT_OF_MEM;
	return gf_list_add(movie->TopBoxes, _free);
}

fixed #1659


const std::string &HttpFile::getFileName() const
{
    return implPtr_->getFileName();
}

Prevent malformed upload path causing arbitrary write (#1174)


int HttpFile::save(const std::string &path) const
{
    return implPtr_->save(path);
}


string_view HttpFile::getFileExtension() const
{
    return implPtr_->getFileExtension();
}


int HttpFileImpl::save() const
{
    return save(HttpAppFrameworkImpl::instance().getUploadPath());
}


int HttpFileImpl::saveTo(const filesystem::path &pathAndFileName) const
{
    LOG_TRACE << "save uploaded file:" << pathAndFileName;
    auto wPath = utils::toNativePath(pathAndFileName.native());
    std::ofstream file(wPath, std::ios::binary);
    if (file.is_open())
    {
        file.write(fileContent_.data(), fileContent_.size());
        file.close();
        return 0;
    }
    else
    {
        LOG_ERROR << "save failed!";
        return -1;
    }
}


void HttpFile::setFileName(const std::string &fileName)
{
    implPtr_->setFileName(fileName);
}


HttpFile::HttpFile(std::shared_ptr<HttpFileImpl> &&implPtr)
    : implPtr_(std::move(implPtr))
{
}


const char *HttpFile::fileData() const noexcept
{
    return implPtr_->fileData();
}


std::string HttpFile::getMd5() const
{
    return implPtr_->getMd5();
}


FileType HttpFile::getFileType() const
{
    return implPtr_->getFileType();
}


DROGON_TEST(HttpFile)
{
    SUBSECTION(Save)
    {
        HttpFileImpl file;
        file.setFileName("test_file_name");
        file.setFile("test", 4);
        auto out = file.save("./test_uploads_dir");

        CHECK(out == 0);
        CHECK(filesystem::exists("./test_uploads_dir/test_file_name"));

        filesystem::remove_all("./test_uploads_dir");
    }

    SUBSECTION(SavePathRelativeTraversal)
    {
        auto uploadPath = filesystem::current_path() / "test_uploads_dir";

        HttpFileImpl file;
        file.setFileName("../test_malicious_file_name");
        file.setFile("test", 4);
        auto out = file.save(uploadPath.string());

        CHECK(out == -1);
        CHECK(!filesystem::exists(uploadPath / "../test_malicious_file_name"));

        filesystem::remove_all(uploadPath);
        filesystem::remove(uploadPath / "../test_malicious_file_name");
    }

    SUBSECTION(SavePathAbsoluteTraversal)
    {
        HttpFileImpl file;
        file.setFileName("/tmp/test_malicious_file_name");
        file.setFile("test", 4);
        auto out = file.save("./test_uploads_dir");

        CHECK(out == -1);
        CHECK(!filesystem::exists("/tmp/test_malicious_file_name"));

        filesystem::remove_all("test_uploads_dir");
        filesystem::remove_all("/tmp/test_malicious_file_name");
    }
}


std::string HttpFileImpl::getMd5() const
{
    return utils::getMd5(fileContent_.data(), fileContent_.size());
}


int HttpFile::saveAs(const std::string &fileName) const
{
    return implPtr_->saveAs(fileName);
}


size_t HttpFile::fileLength() const noexcept
{
    return implPtr_->fileLength();
}


int HttpFileImpl::saveAs(const std::string &fileName) const
{
    assert(!fileName.empty());
    filesystem::path fsFileName(utils::toNativePath(fileName));
    if (!fsFileName.is_absolute() && (!fsFileName.has_parent_path() ||
                                      (fsFileName.begin()->string() != "." &&
                                       fsFileName.begin()->string() != "..")))
    {
        filesystem::path fsUploadPath(utils::toNativePath(
            HttpAppFrameworkImpl::instance().getUploadPath()));
        fsFileName = fsUploadPath / fsFileName;
    }
    if (fsFileName.has_parent_path() &&
        !filesystem::exists(fsFileName.parent_path()))
    {
        LOG_TRACE << "create path:" << fsFileName.parent_path();
        drogon::error_code err;
        filesystem::create_directories(fsFileName.parent_path(), err);
        if (err)
        {
            LOG_SYSERR;
            return -1;
        }
    }
    return saveTo(fsFileName);
}


void HttpFile::setFile(const char *data, size_t length)
{
    implPtr_->setFile(data, length);
}


int HttpFileImpl::save(const std::string &path) const
{
    assert(!path.empty());
    if (fileName_.empty())
        return -1;
    filesystem::path fsUploadDir(utils::toNativePath(path));

    if (!fsUploadDir.is_absolute() && (!fsUploadDir.has_parent_path() ||
                                       (fsUploadDir.begin()->string() != "." &&
                                        fsUploadDir.begin()->string() != "..")))
    {
        fsUploadDir = utils::toNativePath(
                          HttpAppFrameworkImpl::instance().getUploadPath()) /
                      fsUploadDir;
    }

    fsUploadDir = filesystem::weakly_canonical(fsUploadDir);

    if (!filesystem::exists(fsUploadDir))
    {
        LOG_TRACE << "create path:" << fsUploadDir;
        drogon::error_code err;
        filesystem::create_directories(fsUploadDir, err);
        if (err)
        {
            LOG_SYSERR;
            return -1;
        }
    }

    filesystem::path fsSaveToPath(filesystem::weakly_canonical(
        fsUploadDir / utils::toNativePath(fileName_)));

    if (!std::equal(fsUploadDir.begin(),
                    fsUploadDir.end(),
                    fsSaveToPath.begin()))
    {
        LOG_ERROR
            << "Attempt writing outside of upload directory detected. Path: "
            << fileName_;
        return -1;
    }

    return saveTo(fsSaveToPath);
}


const std::string &HttpFile::getItemName() const
{
    return implPtr_->getItemName();
}


int HttpFile::save() const
{
    return implPtr_->save();
}


ModuleExport void UnregisterDCMImage(void)
{
  (void) UnregisterMagickInfo("DCM");
}

Coders: https://github.com/ImageMagick/ImageMagick/issues/4947


static int ReadDCMByte(DCMStreamInfo *stream_info,Image *image)
{
  if (image->compression != RLECompression)
    return(ReadBlobByte(image));
  if (stream_info->count == 0)
    {
      int
        byte;

      ssize_t
        count;

      if (stream_info->remaining <= 2)
        stream_info->remaining=0;
      else
        stream_info->remaining-=2;
      count=(ssize_t) ReadBlobByte(image);
      byte=ReadBlobByte(image);
      if (count == 128)
        return(0);
      else
        if (count < 128)
          {
            /*
              Literal bytes.
            */
            stream_info->count=count;
            stream_info->byte=(-1);
            return(byte);
          }
        else
          {
            /*
              Repeated bytes.
            */
            stream_info->count=256-count;
            stream_info->byte=byte;
            return(byte);
          }
    }
  stream_info->count--;
  if (stream_info->byte >= 0)
    return(stream_info->byte);
  if (stream_info->remaining > 0)
    stream_info->remaining--;
  return(ReadBlobByte(image));
}


ModuleExport size_t RegisterDCMImage(void)
{
  MagickInfo
    *entry;

  static const char
    *DCMNote=
    {
      "DICOM is used by the medical community for images like X-rays.  The\n"
      "specification, \"Digital Imaging and Communications in Medicine\n"
      "(DICOM)\", is available at http://medical.nema.org/.  In particular,\n"
      "see part 5 which describes the image encoding (RLE, JPEG, JPEG-LS),\n"
      "and supplement 61 which adds JPEG-2000 encoding."
    };

  entry=AcquireMagickInfo("DCM","DCM",
    "Digital Imaging and Communications in Medicine image");
  entry->decoder=(DecodeImageHandler *) ReadDCMImage;
  entry->magick=(IsImageFormatHandler *) IsDCM;
  entry->flags^=CoderAdjoinFlag;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->note=ConstantString(DCMNote);
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}


static inline void RelinquishDCMMemory(DCMInfo *info,DCMMap *map,
  DCMStreamInfo *stream_info,LinkedListInfo *stack,unsigned char *data)
{
  if (info->scale != (Quantum *) NULL)
    info->scale=(Quantum *) RelinquishMagickMemory(info->scale);
  if (map->gray != (int *) NULL)
    map->gray=(int *) RelinquishMagickMemory(map->gray);
  if (map->blue != (int *) NULL)
    map->blue=(int *) RelinquishMagickMemory(map->blue);
  if (map->green != (int *) NULL)
    map->green=(int *) RelinquishMagickMemory(map->green);
  if (map->red != (int *) NULL)
    map->red=(int *) RelinquishMagickMemory(map->red);
  if (stream_info->offsets != (ssize_t *) NULL)
    stream_info->offsets=(ssize_t *) RelinquishMagickMemory(
      stream_info->offsets);
  if (stream_info != (DCMStreamInfo *) NULL)
    stream_info=(DCMStreamInfo *) RelinquishMagickMemory(stream_info);
  if (stack != (LinkedListInfo *) NULL)
    stack=DestroyLinkedList(stack,RelinquishDCMInfo);
  if (data != (unsigned char *) NULL)
    data=(unsigned char *) RelinquishMagickMemory(data);
}


static MagickBooleanType ReadDCMPixels(Image *image,DCMInfo *info,
  DCMStreamInfo *stream_info,MagickBooleanType first_segment,
  ExceptionInfo *exception)
{
  int
    byte,
    index;

  MagickBooleanType
    status;

  PixelPacket
    pixel;

  ssize_t
    i,
    x;

  Quantum
    *q;

  ssize_t
    y;

  /*
    Convert DCM Medical image to pixel packets.
  */
  byte=0;
  i=0;
  status=MagickTrue;
  (void) memset(&pixel,0,sizeof(pixel));
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      break;
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      if (info->samples_per_pixel == 1)
        {
          int
            pixel_value;

          if (info->bytes_per_pixel == 1)
            pixel_value=info->polarity != MagickFalse ?
              ((int) info->max_value-ReadDCMByte(stream_info,image)) :
              ReadDCMByte(stream_info,image);
          else
            if ((info->bits_allocated != 12) || (info->significant_bits != 12))
              {
                if (info->signed_data != 0)
                  pixel_value=ReadDCMSignedShort(stream_info,image);
                else
                  pixel_value=(int) ReadDCMShort(stream_info,image);
                if (info->polarity != MagickFalse)
                  pixel_value=(int)info->max_value-pixel_value;
              }
            else
              {
                if ((i & 0x01) != 0)
                  {
                    pixel_value=byte;
                    byte=ReadDCMByte(stream_info,image);
                    if (byte >= 0)
                      pixel_value|=(byte << 8);
                  }
                else
                  {
                    pixel_value=ReadDCMSignedShort(stream_info,image);
                    byte=(int) (pixel_value & 0x0f);
                    pixel_value>>=4;
                  }
                i++;
              }
          if (info->signed_data == 1)
            pixel_value-=32767;
          index=pixel_value;
          if (info->rescale != MagickFalse)
            {
              double
                scaled_value;

              scaled_value=pixel_value*info->rescale_slope+
                info->rescale_intercept;
              index=(int) scaled_value;
              if (info->window_width != 0)
                {
                  double
                    window_max,
                    window_min;

                  window_min=ceil(info->window_center-
                    (info->window_width-1.0)/2.0-0.5);
                  window_max=floor(info->window_center+
                    (info->window_width-1.0)/2.0+0.5);
                  if (scaled_value <= window_min)
                    index=0;
                  else
                    if (scaled_value > window_max)
                      index=(int) info->max_value;
                    else
                      index=(int) (info->max_value*(((scaled_value-
                        info->window_center-0.5)/(info->window_width-1))+0.5));
                }
            }
          index&=info->mask;
          index=(int) ConstrainColormapIndex(image,(ssize_t) index,exception);
          if (first_segment != MagickFalse)
            SetPixelIndex(image,(Quantum) index,q);
          else
            SetPixelIndex(image,(Quantum) (((size_t) index) |
              (((size_t) GetPixelIndex(image,q)) << 8)),q);
          pixel.red=(unsigned int) image->colormap[index].red;
          pixel.green=(unsigned int) image->colormap[index].green;
          pixel.blue=(unsigned int) image->colormap[index].blue;
        }
      else
        {
          if (info->bytes_per_pixel == 1)
            {
              pixel.red=(unsigned int) ReadDCMByte(stream_info,image);
              pixel.green=(unsigned int) ReadDCMByte(stream_info,image);
              pixel.blue=(unsigned int) ReadDCMByte(stream_info,image);
            }
          else
            {
              pixel.red=ReadDCMShort(stream_info,image);
              pixel.green=ReadDCMShort(stream_info,image);
              pixel.blue=ReadDCMShort(stream_info,image);
            }
          pixel.red&=info->mask;
          pixel.green&=info->mask;
          pixel.blue&=info->mask;
          if (info->scale != (Quantum *) NULL)
            {
              if ((MagickSizeType) pixel.red <= GetQuantumRange(info->depth))
                pixel.red=(unsigned int) info->scale[pixel.red];
              if ((MagickSizeType) pixel.green <= GetQuantumRange(info->depth))
                pixel.green=(unsigned int) info->scale[pixel.green];
              if ((MagickSizeType) pixel.blue <= GetQuantumRange(info->depth))
                pixel.blue=(unsigned int) info->scale[pixel.blue];
            }
        }
      if (first_segment != MagickFalse)
        {
          SetPixelRed(image,(Quantum) pixel.red,q);
          SetPixelGreen(image,(Quantum) pixel.green,q);
          SetPixelBlue(image,(Quantum) pixel.blue,q);
        }
      else
        {
          SetPixelRed(image,(Quantum) (((size_t) pixel.red) |
            (((size_t) GetPixelRed(image,q)) << 8)),q);
          SetPixelGreen(image,(Quantum) (((size_t) pixel.green) |
            (((size_t) GetPixelGreen(image,q)) << 8)),q);
          SetPixelBlue(image,(Quantum) (((size_t) pixel.blue) |
            (((size_t) GetPixelBlue(image,q)) << 8)),q);
        }
      q+=GetPixelChannels(image);
    }
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
      break;
    if (image->previous == (Image *) NULL)
      {
        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
          image->rows);
        if (status == MagickFalse)
          break;
      }
  }
  return(status);
}