static void compress_moov(struct mp4_context_t* mp4_context,
                          struct moov_t* moov,
                          unsigned char* moov_data,
                          uint64_t* moov_size)
{
  uLong sourceLen = (uLong)(*moov_size - ATOM_PREAMBLE_SIZE);
  uLong destLen = compressBound(sourceLen);
  unsigned char* cmov = (unsigned char*)malloc(destLen);
  int zstatus = compress(cmov, &destLen, moov_data, sourceLen);
  if(zstatus == Z_OK)
  {
    MP4_INFO("cmov size = %lu (%ld%%)\n", destLen, 100 * destLen / sourceLen);
  }

  {
    const int extra_space = 4096;
    if(destLen + extra_space < sourceLen)
    {
      const int bytes_saved = sourceLen - destLen;
      uLong destLen2;
      int extra = 0;
      MP4_INFO("shifting offsets by %d\n", -bytes_saved);
      moov_shift_offsets_inplace(moov, -bytes_saved);

      extra += ATOM_PREAMBLE_SIZE + 4;            // dcom
      extra += ATOM_PREAMBLE_SIZE + 4;            // cmvd
      extra += ATOM_PREAMBLE_SIZE;                // cmov
      extra += ATOM_PREAMBLE_SIZE + extra_space;  // free

      MP4_INFO("shifting offsets by %d\n", extra);
      moov_shift_offsets_inplace(moov, extra);

      // recompress
      destLen2 = compressBound(sourceLen);
      zstatus = compress(cmov, &destLen2, moov_data, sourceLen);
      if(zstatus == Z_OK)
      {
        MP4_INFO("cmov size = %lu (%ld%%)\n", destLen2, 100 * destLen2 / sourceLen);

        if(destLen2 < destLen + extra_space)
        {
          // copy compressed movie atom
          unsigned char* outbuffer = moov_data;

          uint32_t dcom_size = ATOM_PREAMBLE_SIZE + 4;
          uint32_t cmvd_size = ATOM_PREAMBLE_SIZE + 4 + destLen2;
          uint32_t cmov_size = ATOM_PREAMBLE_SIZE + dcom_size + cmvd_size;
          uint32_t free_size = ATOM_PREAMBLE_SIZE + extra_space + destLen - destLen2;
          *moov_size = ATOM_PREAMBLE_SIZE + cmov_size + free_size;

          outbuffer = write_32(outbuffer, (uint32_t)*moov_size);

          // skip 'moov'
          outbuffer += 4;

          outbuffer = write_32(outbuffer, cmov_size);
          {
            outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'o', 'v'));
            outbuffer = write_32(outbuffer, dcom_size);
            outbuffer = write_32(outbuffer, FOURCC('d', 'c', 'o', 'm'));
            outbuffer = write_32(outbuffer, FOURCC('z', 'l', 'i', 'b'));

            outbuffer = write_32(outbuffer, cmvd_size);
            {
              outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'v', 'd'));
              outbuffer = write_32(outbuffer, sourceLen);
              memcpy(outbuffer, cmov, destLen2);
              outbuffer += destLen2;
            }
          }

          // add final padding
          outbuffer = write_32(outbuffer, free_size);
          outbuffer = write_32(outbuffer, FOURCC('f', 'r', 'e', 'e'));
          {
            const char free_bytes[8] =
            {
              'C', 'o', 'd', 'e','S','h', 'o', 'p'
            };
            uint32_t padding_index;
            for(padding_index = ATOM_PREAMBLE_SIZE; padding_index != free_size; ++padding_index)
            {
              outbuffer[padding_index] = free_bytes[padding_index % 8];
            }
          }
        }
        else
        {
          MP4_ERROR("%s", "2nd pass compress overflow\n");
        }
      }
    }
  }
  free(cmov);
}
extern int output_mp4(struct mp4_context_t* mp4_context,
                      unsigned int const* trak_sample_start,
                      unsigned int const* trak_sample_end,
                      struct bucket_t** buckets,
                      struct mp4_split_options_t* options)
{
  unsigned int i;

  uint64_t mdat_start = mp4_context->mdat_atom.start_;
  uint64_t mdat_size = mp4_context->mdat_atom.size_;
  int64_t offset;

  struct moov_t* moov = mp4_context->moov;
//  unsigned char* moov_data = mp4_context->moov_data;
  unsigned char* moov_data = (unsigned char*)
    malloc((size_t)mp4_context->moov_atom.size_ + ATOM_PREAMBLE_SIZE + 1024);

  uint64_t moov_size;

  long moov_time_scale = moov->mvhd_->timescale_;
  uint64_t skip_from_start = UINT64_MAX;
  uint64_t end_offset = 0;

  uint64_t moov_duration = 0;

#if 1
  uint64_t new_mdat_start = 0;
  {
    static char const free_data[] = {
      0x0, 0x0, 0x0,  42, 'f', 'r', 'e', 'e',
      'v', 'i', 'd', 'e', 'o', ' ', 's', 'e',
      'r', 'v', 'e', 'd', ' ', 'b', 'y', ' ',
      'm', 'o', 'd', '_', 'h', '2', '6', '4',
      '_', 's', 't', 'r', 'e', 'a', 'm', 'i',
      'n', 'g'
    };
    uint32_t size_of_header = (uint32_t)mp4_context->ftyp_atom.size_ +
                              sizeof(free_data);
    unsigned char* buffer = (unsigned char*)malloc(size_of_header);

    if(mp4_context->ftyp_atom.size_)
    {
      fseeko(mp4_context->infile, mp4_context->ftyp_atom.start_, SEEK_SET);
      if(fread(buffer, (off_t)mp4_context->ftyp_atom.size_, 1, mp4_context->infile) != 1)
      {
        MP4_ERROR("%s", "Error reading ftyp atom\n");
        free(buffer);
        return 0;
      }
    }

    // copy free data
    memcpy(buffer + mp4_context->ftyp_atom.size_, free_data,
           sizeof(free_data));

    if(options->output_format == OUTPUT_FORMAT_MP4)
    {
      bucket_t* bucket = bucket_init_memory(buffer, size_of_header);
      bucket_insert_tail(buckets, bucket);
    }
    free(buffer);

    new_mdat_start += size_of_header;
  }

//  new_mdat_start += mp4_context->moov_atom.size_;
#endif

  offset = new_mdat_start - mp4_context->mdat_atom.start_;
  // subtract old moov size
//  offset -= mp4_context->moov_atom.size_;

  for(i = 0; i != moov->tracks_; ++i)
  {
    struct trak_t* trak = moov->traks_[i];
    struct stbl_t* stbl = trak->mdia_->minf_->stbl_;

    unsigned int start_sample = trak_sample_start[i];
    unsigned int end_sample = trak_sample_end[i];


    if (options->exact)
      trak_fast_forward_first_partial_GOP(mp4_context, options, trak, start_sample);

    trak_update_index(mp4_context, trak, start_sample, end_sample);

    if(trak->samples_size_ == 0)
    {
      MP4_WARNING("Trak %u contains no samples. Maybe a fragmented file?", i);
      return 1;
    }

    {
      uint64_t skip =
        trak->samples_[start_sample].pos_ - trak->samples_[0].pos_;
      if(skip < skip_from_start)
        skip_from_start = skip;
      MP4_INFO("Trak can skip %"PRIu64" bytes\n", skip);

      if(end_sample != trak->samples_size_)
      {
        uint64_t end_pos = trak->samples_[end_sample].pos_;
        if(end_pos > end_offset)
          end_offset = end_pos;
        MP4_INFO("New endpos=%"PRIu64"\n", end_pos);
        MP4_INFO("Trak can skip %"PRIu64" bytes at end\n",
               mdat_start + mdat_size - end_offset);
      }
    }

    {
      // fixup trak (duration)
      uint64_t trak_duration = stts_get_duration(stbl->stts_);
      long trak_time_scale = trak->mdia_->mdhd_->timescale_;
      {
        uint64_t duration = trak_time_to_moov_time(trak_duration,
          moov_time_scale, trak_time_scale);
        trak->mdia_->mdhd_->duration_= trak_duration;
        trak->tkhd_->duration_ = duration;
        MP4_INFO("trak: new_duration=%"PRIu64"\n", duration);

        if(duration > moov_duration)
          moov_duration = duration;
      }
    }

//      MP4_INFO("stco.size=%d, ", read_int32(stbl->stco_ + 4));
//      MP4_INFO("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_));
//      MP4_INFO("stsz.size=%d\n", read_int32(stbl->stsz_ + 8));
//      MP4_INFO("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_));
  }
  moov->mvhd_->duration_ = moov_duration;
  MP4_INFO("moov: new_duration=%.2f seconds\n", moov_duration / (float)moov_time_scale);

  // subtract bytes we skip at the front of the mdat atom
  offset -= skip_from_start;

  MP4_INFO("%s", "moov: writing header\n");

  moov_write(moov, moov_data);
  moov_size = read_32(moov_data);

  // add new moov size
  offset += moov_size;

  MP4_INFO("shifting offsets by %"PRId64"\n", offset);
  moov_shift_offsets_inplace(moov, offset);

  // traffic shaping: create offsets for each second
  create_traffic_shaping(moov,
                         trak_sample_start,
                         trak_sample_end,
                         offset,
                         options);

#ifdef COMPRESS_MOOV_ATOM
  if(!options->client_is_flash)
  {
    compress_moov(mp4_context, moov, moov_data, &moov_size);
  }
#endif

  if(end_offset != 0)
  {
    MP4_INFO("mdat_size=%"PRId64" end_offset=%"PRId64"\n",
             mdat_size, end_offset);
    mdat_size = end_offset - mdat_start;
  }
  mdat_start += skip_from_start;
  mdat_size -= skip_from_start;

  MP4_INFO("mdat_bucket(%"PRId64", %"PRId64")\n", mdat_start, mdat_size);

  bucket_insert_tail(buckets, bucket_init_memory(moov_data, moov_size));
  free(moov_data);

  {
    struct mp4_atom_t mdat_atom;
    mdat_atom.type_ = FOURCC('m', 'd', 'a', 't');
    mdat_atom.short_size_ = 0; // TODO: use original small/wide mdat box

    if(options->adaptive)
    {
      // empty mdat atom
      mdat_atom.size_ = ATOM_PREAMBLE_SIZE;
    }
    else
    {
      mdat_atom.size_ = mdat_size;
    }

    {
      unsigned char buffer[32];
      int mdat_header_size = mp4_atom_write_header(buffer, &mdat_atom);
      bucket_insert_tail(buckets,
        bucket_init_memory(buffer, mdat_header_size));

      if(mdat_atom.size_ - mdat_header_size)
      {
        bucket_insert_tail(buckets,
          bucket_init_file(mdat_start + mdat_header_size,
                           mdat_atom.size_ - mdat_header_size));
      }
    }
  }

  return 1;
}
Пример #3
0
static int moof_create(struct mp4_context_t const* mp4_context,
struct moov_t* fmoov,
struct woov_t* woov,
struct moof_t* moof,
struct mfra_t* mfra,
	uint64_t moof_offset,
	unsigned int seq,
struct bucket_t** buckets,
	int output_raw)
{
	uint32_t mdat_size = ATOM_PREAMBLE_SIZE;
	bucket_t* mdat_bucket = 0;
	unsigned int i = 0;
	uint64_t start_time = 0, end_time = 0;
	unsigned int start=0, end=0;
	if(!output_raw)
	{
		unsigned char mdat_buffer[32];
		mp4_atom_t mdat_atom;
		int mdat_header_size;
		mdat_atom.type_ = FOURCC('m', 'd', 'a', 't');
		mdat_atom.short_size_ = 0;
		mdat_header_size = mp4_atom_write_header(mdat_buffer, &mdat_atom);
		mdat_bucket = bucket_init_memory(mdat_buffer, mdat_header_size);
		bucket_insert_tail(buckets, mdat_bucket);
	}

	moof->mfhd_ = mfhd_init();
	moof->mfhd_->sequence_number_ = seq;

	for(i = 0;i < fmoov->tracks_; i++)
	{
		uint32_t trun_mdat_size=0;
		struct trak_t * trak = fmoov->traks_[i];
		struct stsd_t const* stsd = trak->mdia_->minf_->stbl_->stsd_;
		struct sample_entry_t const* sample_entry = &stsd->sample_entries_[0];
		//    int is_avc = sample_entry->fourcc_ == FOURCC('a', 'v', 'c', '1');

		struct traf_t* traf = traf_init();
		moof->trafs_[moof->tracks_] = traf;
		++moof->tracks_;

		start = trak->smoothes_[moof->mfhd_->sequence_number_-1].start;
		end = trak->smoothes_[moof->mfhd_->sequence_number_].start;


		{
			//      struct ctts_t const* ctts = trak->mdia_->minf_->stbl_->ctts_;
			unsigned int trun_index = 0;
			unsigned int s;
			struct bucket_t* bucket_prev = 0;

			traf->tfhd_ = tfhd_init();
			// 0x000020 = default-sample-flags present
			traf->tfhd_->flags_ = 0x000020;
			traf->tfhd_->track_id_ = trak->tkhd_->track_id_;
			// sample_degradation_priority
			traf->tfhd_->default_sample_flags_ = 0x000000;
			// sample_is_difference_sample
			if(trak->mdia_->hdlr_->handler_type_ == FOURCC('v', 'i', 'd', 'e'))
			{
				traf->tfhd_->default_sample_flags_ |= (1 << 16);
			}

			traf->trun_ = trun_init();
			// 0x0001 = data-offset is present
			// 0x0004 = first_sample_flags is present
			// 0x0100 = sample-duration is present
			// 0x0200 = sample-size is present
			traf->trun_->flags_ = 0x000305;
			// 0x0800 = sample-composition-time-offset is present
			//      if(ctts)
			{
				traf->trun_->flags_ |= 0x000800;
			}

			traf->trun_->sample_count_ = end - start;
			//    traf->trun_->data_offset_ = // set below
			traf->trun_->first_sample_flags_= 0x00000000;
			traf->trun_->table_ = (trun_table_t*)malloc(traf->trun_->sample_count_ * sizeof(trun_table_t));

			//      traf->trun_->trak_ = trak;
			//      traf->trun_->start_ = start;
			//    traf->trun_->uuid0_pts_ = trak_time_to_moov_time(
			//        trak->samples_[start].pts_, 10000000, trak->mdia_->mdhd_->timescale_);

			for(s = start; s != end; ++s)
			{
				uint64_t pts1 = trak->samples_[s + 1].pts_;
				uint64_t pts0 = trak->samples_[s + 0].pts_;

				unsigned int sample_duration = (unsigned int)(pts1 - pts0);

				uint64_t sample_pos = trak->samples_[s].pos_;
				unsigned int sample_size = trak->samples_[s].size_;
				unsigned int cto = trak->samples_[s].cto_;

				traf->trun_->table_[trun_index].sample_duration_ = sample_duration;
				traf->trun_->table_[trun_index].sample_size_ = sample_size;
				traf->trun_->table_[trun_index].sample_composition_time_offset_ = cto;

				MP4_INFO(
					"frame=%u pts=%"PRIi64" cto=%u duration=%u offset=%"PRIu64" size=%u\n",
					s,
					trak->samples_[s].pts_,
					trak->samples_[s].cto_,
					sample_duration,
					sample_pos, sample_size);

				if(trak->mdia_->hdlr_->handler_type_ == FOURCC('v', 'i', 'd', 'e'))
				{
#if 0
					if(bucket_prev == NULL)
					{
						// TODO: return error when no SPS and PPS are available
						if(is_avc)
						{
							unsigned char* buffer;
							unsigned char* p;
							unsigned int sps_pps_size =
								sample_entry->nal_unit_length_ + sample_entry->sps_length_ +
								sample_entry->nal_unit_length_ + sample_entry->pps_length_;

							if(sps_pps_size == 0)
							{
								MP4_ERROR("%s", "[Error] No SPS or PPS available\n");
								return 0;
							}

							buffer = (unsigned char*)malloc(sps_pps_size);
							p = buffer;

							// sps
							p = write_32(p, 0x00000001);
							memcpy(p, sample_entry->sps_, sample_entry->sps_length_);
							p += sample_entry->sps_length_;

							// pps
							p = write_32(p, 0x00000001);
							memcpy(p, sample_entry->pps_, sample_entry->pps_length_);
							p += sample_entry->pps_length_;

							bucket_insert_tail(buckets, bucket_init_memory(buffer, sps_pps_size));
							free(buffer);

							traf->trun_->table_[trun_index].sample_size_ += sps_pps_size;
							mdat_size += sps_pps_size;
							trun_mdat_size += sps_pps_size;
						}
					}
#endif

#if 0
					if(is_avc)
					{
						static const char nal_marker[4] = { 0, 0, 0, 1 };
						uint64_t first = sample_pos;
						uint64_t last = sample_pos + sample_size;
						while(first != last)
						{
							unsigned char buffer[4];
							unsigned int nal_size;
							bucket_insert_tail(buckets, bucket_init_memory(nal_marker, 4));

							if(fseeko(mp4_context->infile, first, SEEK_SET) != 0)
							{
								MP4_ERROR("%s", "Reached end of file prematurely\n");
								return 0;
							}
							if(fread(buffer, sample_entry->nal_unit_length_, 1, mp4_context->infile) != 1)
							{
								MP4_ERROR("%s", "Error reading NAL size\n");
								return 0;
							}
							nal_size = read_n(buffer, sample_entry->nal_unit_length_ * 8);

							if(nal_size == 0)
							{
								MP4_ERROR("%s", "Invalid NAL size (0)\n");
								return 0;
							}

							bucket_prev = bucket_init_file(first + sample_entry->nal_unit_length_, nal_size);
							bucket_insert_tail(buckets, bucket_prev);

							first += sample_entry->nal_unit_length_ + nal_size;
						}
					}
					else
#endif
					{
						// try to merge buckets
						if(bucket_prev &&
							sample_pos == bucket_prev->offset_ + bucket_prev->size_)
						{
							bucket_prev->size_ += sample_size;
						}
						else
						{
							bucket_prev = bucket_init_file(sample_pos, sample_size);
							bucket_insert_tail(buckets, bucket_prev);
						}
					}
				} else
					if(trak->mdia_->hdlr_->handler_type_ == FOURCC('s', 'o', 'u', 'n'))
					{
						// ADTS frame header
						if(sample_entry->wFormatTag == 0x00ff && output_raw)
						{
							unsigned char buffer[7];
							sample_entry_get_adts(sample_entry, sample_size, buffer);

							bucket_insert_tail(buckets, bucket_init_memory(buffer, 7));

							traf->trun_->table_[trun_index].sample_size_ += 7;
							mdat_size += 7;
							trun_mdat_size += 7;

							bucket_prev = NULL;
						}

						// try to merge buckets
						if(bucket_prev &&
							sample_pos == bucket_prev->offset_ + bucket_prev->size_)
						{
							bucket_prev->size_ += sample_size;
						}
						else
						{
							bucket_prev = bucket_init_file(sample_pos, sample_size);
							bucket_insert_tail(buckets, bucket_prev);
						}
					}

					mdat_size += sample_size;
					trun_mdat_size += sample_size;

					++trun_index;

					{
						// update woov track samples
						woov->moov->traks_[moof->tracks_-1]->samples_[woov->moov->traks_[moof->tracks_-1]->samples_size_++]=trak->samples_[s];
						woov->moov->traks_[moof->tracks_-1]->samples_[woov->moov->traks_[moof->tracks_-1]->samples_size_]=trak->samples_[s+1];
					}
			}
			{

				struct tfra_t* tfra = mfra->tfras_[moof->tracks_-1];
				tfra_table_t* table = &tfra->table_[moof->mfhd_->sequence_number_-1];
				table->time_ = trak->samples_[start].pts_;
				table->moof_offset_ = moof_offset;
				table->traf_number_ = moof->tracks_-1;
				table->trun_number_ = 0;
				table->sample_number_ = 0;
				stco_add_chunk(woov->moov->traks_[moof->tracks_-1]->mdia_->minf_->stbl_->stco_, woov->mdat_size);
				woov->mdat_size += trun_mdat_size;
				stsc_add_chunk(woov->moov->traks_[moof->tracks_-1]->mdia_->minf_->stbl_->stsc_, 
					woov->moov->traks_[moof->tracks_-1]->mdia_->minf_->stbl_->stco_->entries_-1, end-start, 1); 

			}
			// update size of mdat atom
			if(mdat_bucket)
			{
				write_32((unsigned char*)mdat_bucket->buf_, mdat_size);
			}
		}
	}

	return 1;
}