Пример #1
0
GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode)
{
    GF_Box *a;
    u64 totSize;
    GF_Err e = GF_OK;

    totSize = 0;


#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
    if (mov->single_moof_mode && mov->single_moof_state == 2) {
        return e;
    }

    /*restart from where we stopped last*/
    totSize = mov->current_top_box_start;
    gf_bs_seek(mov->movieFileMap->bs, mov->current_top_box_start);

#endif


    /*while we have some data, parse our boxes*/
    while (gf_bs_available(mov->movieFileMap->bs)) {
        *bytesMissing = 0;
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
        mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs);
        GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Current top box start before parsing %d\n", mov->current_top_box_start));
#endif

        e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing, progressive_mode);

        if (e >= 0) {
            e = GF_OK;
        } else if (e == GF_ISOM_INCOMPLETE_FILE) {
            /*our mdat is uncomplete, only valid for READ ONLY files...*/
            if (mov->openMode != GF_ISOM_OPEN_READ) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n"));
                return GF_ISOM_INVALID_FILE;
            }
            return e;
        } else {
            return e;
        }

        switch (a->type) {
        /*MOOV box*/
        case GF_ISOM_BOX_TYPE_MOOV:
            if (mov->moov) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n"));
                return GF_ISOM_INVALID_FILE;
            }
            mov->moov = (GF_MovieBox *)a;
            /*set our pointer to the movie*/
            mov->moov->mov = mov;
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
            if (mov->moov->mvex) mov->moov->mvex->mov = mov;
#endif
            e = gf_list_add(mov->TopBoxes, a);
            if (e) {
                return e;
            }
            totSize += a->size;
            break;

        /*META box*/
        case GF_ISOM_BOX_TYPE_META:
            if (mov->meta) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n"));
                return GF_ISOM_INVALID_FILE;
            }
            mov->meta = (GF_MetaBox *)a;
            e = gf_list_add(mov->TopBoxes, a);
            if (e) {
                return e;
            }
            totSize += a->size;
            break;

        /*we only keep the MDAT in READ for dump purposes*/
        case GF_ISOM_BOX_TYPE_MDAT:
            totSize += a->size;
            if (mov->openMode == GF_ISOM_OPEN_READ) {
                if (!mov->mdat) {
                    mov->mdat = (GF_MediaDataBox *) a;
                    e = gf_list_add(mov->TopBoxes, mov->mdat);
                    if (e) {
                        return e;
                    }
                }
#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
                else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a);
#endif
                else gf_isom_box_del(a);
            }
            /*if we don't have any MDAT yet, create one (edit-write mode)
            We only work with one mdat, but we're puting it at the place
            of the first mdat found when opening a file for editing*/
            else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) {
                gf_isom_box_del(a);
                mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
                e = gf_list_add(mov->TopBoxes, mov->mdat);
                if (e) {
                    return e;
                }
            } else {
                gf_isom_box_del(a);
            }
            break;
        case GF_ISOM_BOX_TYPE_FTYP:
            /*ONE AND ONLY ONE FTYP*/
            if (mov->brand) {
                gf_isom_box_del(a);
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate FTYP detected!\n"));
                return GF_ISOM_INVALID_FILE;
            }
            mov->brand = (GF_FileTypeBox *)a;
            totSize += a->size;
            e = gf_list_add(mov->TopBoxes, a);
            break;

        case GF_ISOM_BOX_TYPE_PDIN:
            /*ONE AND ONLY ONE PDIN*/
            if (mov->pdin) {
                gf_isom_box_del(a);
                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate PDIN detected!\n"));
                return GF_ISOM_INVALID_FILE;
            }
            mov->pdin = (GF_ProgressiveDownloadBox *) a;
            totSize += a->size;
            e = gf_list_add(mov->TopBoxes, a);
            break;


#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
        case GF_ISOM_BOX_TYPE_STYP:
        {
            u32 brand = ((GF_SegmentTypeBox *)a)->majorBrand;
            switch (brand) {
            case GF_4CC('s', 'i', 's', 'x'):
            case GF_4CC('r', 'i', 's', 'x'):
            case GF_4CC('s', 's', 's', 's'):
                mov->is_index_segment = GF_TRUE;
                break;
            default:
                break;
            }
        }
        /*fall-through*/

        case GF_ISOM_BOX_TYPE_SIDX:
            totSize += a->size;
            if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
                e = gf_list_add(mov->TopBoxes, a);
            } else {
                gf_isom_box_del(a);
            }
            break;

        case GF_ISOM_BOX_TYPE_MOOF:
            if (!mov->moov) {
                GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n"));
            }
            if (mov->single_moof_mode) {
                mov->single_moof_state++;
                if (mov->single_moof_state > 1) {
                    gf_isom_box_del(a);
                    return GF_OK;
                }
            }
            ((GF_MovieFragmentBox *)a)->mov = mov;

            totSize += a->size;
            mov->moof = (GF_MovieFragmentBox *) a;
            /*read & debug: store at root level*/
            if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) {
                u32 k;
                gf_list_add(mov->TopBoxes, a);
                /*also update pointers to trex for debug*/
                if (mov->moov) {
                    for (k=0; k<gf_list_count(mov->moof->TrackList); k++) {
                        GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k);
                        if (traf->tfhd) {
                            GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID);
                            u32 j=0;
                            while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) {
                                if (traf->trex->trackID == traf->tfhd->trackID) {
                                    if (!traf->trex->track) traf->trex->track = trak;
                                    break;
                                }
                                traf->trex = NULL;
                            }
                        }
                        //we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly
                        if (traf->trex && traf->trex->track && (traf->piff_sample_encryption || traf->sample_encryption)) {
                            GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID);
                            e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->piff_sample_encryption ? (GF_SampleEncryptionBox *) traf->piff_sample_encryption : traf->sample_encryption);
                        }
                    }
                }
            } else if (mov->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) {
                mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1;
                mov->moof = NULL;
                gf_isom_box_del(a);
            } else {
                /*merge all info*/
                e = MergeFragment((GF_MovieFragmentBox *)a, mov);
                gf_isom_box_del(a);
            }
            break;
#endif
        case GF_4CC('j','P',' ',' '):
        {
            GF_UnknownBox *box = (GF_UnknownBox*)a;
            u8 *c = (u8 *) box->data;
            if ((box->dataSize==4)
                    && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A))
                mov->is_jp2 = 1;

            gf_isom_box_del(a);
        }
        break;

        case GF_ISOM_BOX_TYPE_PRFT:
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
            if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) {
                //keep the last one read
                if (mov->last_producer_ref_time)
                    gf_isom_box_del(a);
                else
                    mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a;
                break;
            }
#endif
        //fallthrough

        default:
            totSize += a->size;
            e = gf_list_add(mov->TopBoxes, a);
            break;
        }

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
        /*remember where we left, in case we append an entire number of movie fragments*/
        mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs);
#endif
    }

    /*we need at least moov or meta*/
    if (!mov->moov && !mov->meta
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
            && !mov->moof && !mov->is_index_segment
#endif
       ) {
        return GF_ISOM_INCOMPLETE_FILE;
    }
    /*we MUST have movie header*/
    if (mov->moov && !mov->moov->mvhd) {
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n"));
        return GF_ISOM_INVALID_FILE;
    }
    /*we MUST have meta handler*/
    if (mov->meta && !mov->meta->handler) {
        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n"));
        return GF_ISOM_INVALID_FILE;
    }

#ifndef GPAC_DISABLE_ISOM_WRITE

    if (mov->moov) {
        /*set the default interleaving time*/
        mov->interleavingTime = mov->moov->mvhd->timeScale;

#ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
        /*in edit mode with successfully loaded fragments, delete all fragment signaling since
        file is no longer fragmented*/
        if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS) && mov->moov->mvex) {
            gf_isom_box_del((GF_Box *)mov->moov->mvex);
            mov->moov->mvex = NULL;
        }
#endif

    }

    //create a default mdat if none was found
    if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) {
        mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT);
        e = gf_list_add(mov->TopBoxes, mov->mdat);
        if (e) return e;
    }
#endif /*GPAC_DISABLE_ISOM_WRITE*/

    return GF_OK;
}
Пример #2
0
/* Rewrite mode:
 * mode = 0: playback
 * mode = 1: streaming
 */
GF_Err gf_isom_nalu_sample_rewrite(GF_MediaBox *mdia, GF_ISOSample *sample, u32 sampleNumber, GF_MPEGVisualSampleEntryBox *entry)
{
	Bool is_hevc = 0;
	GF_Err e = GF_OK;
	GF_ISOSample *ref_samp;
	GF_BitStream *src_bs, *ref_bs, *dst_bs;
	u64 offset;
	u32 ref_nalu_size, data_offset, data_length, copy_size, nal_size, max_size, di, nal_unit_size_field, cur_extract_mode, extractor_mode;
	Bool rewrite_ps, rewrite_start_codes;
	u8 ref_track_ID, ref_track_num;
	s8 sample_offset, nal_type;
	u32 nal_hdr;
	char *buffer;
	GF_ISOFile *file = mdia->mediaTrack->moov->mov;

	src_bs = ref_bs = dst_bs = NULL;
	ref_samp = NULL;
	buffer = NULL;
	rewrite_ps = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG) ? 1 : 0;
	if (! sample->IsRAP) rewrite_ps = 0;
	rewrite_start_codes = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG) ? 1 : 0;
	extractor_mode = mdia->mediaTrack->extractor_mode&0x0000FFFF;

	if (extractor_mode == GF_ISOM_NALU_EXTRACT_INSPECT) {
		if (!rewrite_ps && !rewrite_start_codes)
			return GF_OK;
	}

	if (!entry) return GF_BAD_PARAM;
	nal_unit_size_field = 0;
	/*if svc rewrire*/
	if (entry->svc_config && entry->svc_config->config) nal_unit_size_field = entry->svc_config->config->nal_unit_size;
	/*if mvc rewrire*/

	/*otherwise do nothing*/
	else if (!rewrite_ps && !rewrite_start_codes) {
		return GF_OK;
	}

	if (!nal_unit_size_field) {
		if (entry->avc_config) nal_unit_size_field = entry->avc_config->config->nal_unit_size;
		else if (entry->hevc_config) {
			nal_unit_size_field = entry->hevc_config->config->nal_unit_size;
			is_hevc = 1;
		}
	}
	if (!nal_unit_size_field) return GF_ISOM_INVALID_FILE;

	dst_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
	src_bs = gf_bs_new(sample->data, sample->dataLength, GF_BITSTREAM_READ);
	max_size = 4096;

	/*rewrite start code with NALU delim*/
	if (rewrite_start_codes) {
		gf_bs_write_int(dst_bs, 1, 32);
		if (is_hevc) {
			gf_bs_write_int(dst_bs, 0, 1);
			gf_bs_write_int(dst_bs, GF_HEVC_NALU_ACCESS_UNIT, 6);
			gf_bs_write_int(dst_bs, 0, 9);
			/*pic-type - by default we signal all slice types possible*/
			gf_bs_write_int(dst_bs, 2, 3);
			gf_bs_write_int(dst_bs, 0, 5);
		} else {
			gf_bs_write_int(dst_bs, (sample->data[0] & 0x60) | GF_AVC_NALU_ACCESS_UNIT, 8);
			gf_bs_write_int(dst_bs, 0xF0 , 8); /*7 "all supported NALUs" (=111) + rbsp trailing (10000)*/;
		}
	}

	if (rewrite_ps) {
		if (is_hevc) {
			u32 i, count;
			count = gf_list_count(entry->hevc_config->config->param_array);
			for (i=0; i<count; i++) {
				GF_HEVCParamArray *ar = gf_list_get(entry->hevc_config->config->param_array, i);
				rewrite_nalus_list(ar->nalus, dst_bs, rewrite_start_codes, nal_unit_size_field);
			}

			/*little optimization if we are not asked to start codes: copy over the sample*/
			if (!rewrite_start_codes) {
				gf_bs_write_data(dst_bs, sample->data, sample->dataLength);
				gf_free(sample->data);
				sample->data = NULL;
				gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength);
				gf_bs_del(src_bs);
				gf_bs_del(dst_bs);
				return GF_OK;
			}
		} else {

			/*this is an SVC track: get all SPS/PPS from this track down to the base layer and rewrite them*/
			if (mdia->mediaTrack->has_base_layer) {
				u32 j;
				GF_List *nalu_sps = gf_list_new();
				GF_List *nalu_pps = gf_list_new();
				GF_TrackReferenceTypeBox *dpnd = NULL;
				Track_FindRef(mdia->mediaTrack, GF_ISOM_REF_SCAL, &dpnd);

#if 0
				/*get all upper layers with SCAL reference to this track*/
				for (j = 0; j < gf_isom_get_track_count(file); j++) {
					if (gf_isom_has_track_reference(file, j+1, GF_ISOM_REF_SCAL, mdia->mediaTrack->Header->trackID)) {
						u32 tkID;
						GF_TrackBox *base_track;
						GF_MPEGVisualSampleEntryBox *base_entry;
						gf_isom_get_reference_ID(file, j+1, GF_ISOM_REF_SCAL, 1, &tkID);

						base_track = GetTrackbyID(mdia->mediaTrack->moov, tkID);
						base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL;
						if (base_entry)
							merge_nalus(base_entry, nalu_sps, nalu_pps);
					}
				}

#endif

				merge_nalus(entry, nalu_sps, nalu_pps);
				if (dpnd) {
					for (j=0; j<dpnd->trackIDCount; j++) {
						GF_TrackBox *base_track = GetTrackbyID(mdia->mediaTrack->moov, dpnd->trackIDs[j]);
						GF_MPEGVisualSampleEntryBox *base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL;
						if (base_entry)
							merge_nalus(base_entry, nalu_sps, nalu_pps);
					}
				}

				//rewrite nalus
				rewrite_nalus_list(nalu_sps, dst_bs, rewrite_start_codes, nal_unit_size_field);
				rewrite_nalus_list(nalu_pps, dst_bs, rewrite_start_codes, nal_unit_size_field);

				gf_list_del(nalu_sps);
				gf_list_del(nalu_pps);
			} else {

				if (entry->avc_config) {
					rewrite_nalus_list(entry->avc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field);
					rewrite_nalus_list(entry->avc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field);
					rewrite_nalus_list(entry->avc_config->config->sequenceParameterSetExtensions, dst_bs, rewrite_start_codes, nal_unit_size_field);
				}

				/*add svc config */
				if (entry->svc_config) {
					rewrite_nalus_list(entry->svc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field);
					rewrite_nalus_list(entry->svc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field);
				}

				/*little optimization if we are not asked to rewrite extractors or start codes: copy over the sample*/
				if (!entry->svc_config && !rewrite_start_codes) {
					gf_bs_write_data(dst_bs, sample->data, sample->dataLength);
					gf_free(sample->data);
					sample->data = NULL;
					gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength);
					gf_bs_del(src_bs);
					gf_bs_del(dst_bs);
					return GF_OK;
				}

			}
		}
	}

	buffer = (char *)gf_malloc(sizeof(char)*max_size);

	while (gf_bs_available(src_bs))
	{
		nal_size = gf_bs_read_int(src_bs, 8*nal_unit_size_field);
		if (nal_size>max_size) {
			buffer = (char*) gf_realloc(buffer, sizeof(char)*nal_size);
			max_size = nal_size;
		}
		if (is_hevc) {
			nal_hdr = gf_bs_read_u16(src_bs);
			nal_type = (nal_hdr&0x7E00) >> 9;
		} else {
			nal_hdr = gf_bs_read_u8(src_bs);
			nal_type = nal_hdr & 0x1F;
		}

		if (is_hevc) {
			/*we already wrote this stuff*/
			if (nal_type==GF_HEVC_NALU_ACCESS_UNIT) {
				gf_bs_skip_bytes(src_bs, nal_size-2);
				continue;
			}

			/*rewrite nal*/
			gf_bs_read_data(src_bs, buffer, nal_size-2);
			if (rewrite_start_codes)
				gf_bs_write_u32(dst_bs, 1);
			else
				gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field);

			gf_bs_write_u16(dst_bs, nal_hdr);
			gf_bs_write_data(dst_bs, buffer, nal_size-2);

			continue;
		}

		/*we already wrote this stuff*/
		if (nal_type==GF_AVC_NALU_ACCESS_UNIT) {
			gf_bs_skip_bytes(src_bs, nal_size-1);
			continue;
		}

		//extractor
		if (nal_type == 31) {
			switch (extractor_mode) {
			case 0:
				gf_bs_read_int(src_bs, 24); //3 bytes of NALUHeader in extractor
				ref_track_ID = gf_bs_read_u8(src_bs);
				sample_offset = (s8) gf_bs_read_int(src_bs, 8);
				data_offset = gf_bs_read_u32(src_bs);
				data_length = gf_bs_read_u32(src_bs);

				ref_track_num = gf_isom_get_track_by_id(file, ref_track_ID);
				if (!ref_track_num) {
					e = GF_BAD_PARAM;
					goto exit;
				}
				cur_extract_mode = gf_isom_get_nalu_extract_mode(file, ref_track_num);
				gf_isom_set_nalu_extract_mode(file, ref_track_num, GF_ISOM_NALU_EXTRACT_INSPECT);
				ref_samp = gf_isom_get_sample(file, ref_track_num, sampleNumber+sample_offset, &di);
				if (!ref_samp) {
					e = GF_IO_ERR;
					goto exit;
				}
				ref_bs = gf_bs_new(ref_samp->data, ref_samp->dataLength, GF_BITSTREAM_READ);
				offset = 0;
				while (gf_bs_available(ref_bs)) {
					if (gf_bs_get_position(ref_bs) < data_offset) {
						ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field);
						offset += ref_nalu_size + nal_unit_size_field;
						if ((offset > data_offset) || (offset >= gf_bs_get_size(ref_bs))) {
							e = GF_BAD_PARAM;
							goto exit;
						}

						e = gf_bs_seek(ref_bs, offset);
						if (e)
							goto exit;
						continue;
					}
					ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field);
					copy_size = data_length ? data_length : ref_nalu_size;
					assert(copy_size <= ref_nalu_size);
					nal_hdr = gf_bs_read_u8(ref_bs); //rewrite NAL type
					if ((copy_size-1)>max_size) {
						buffer = (char*)gf_realloc(buffer, sizeof(char)*(copy_size-1));
						max_size = copy_size-1;
					}
					gf_bs_read_data(ref_bs, buffer, copy_size-1);

					if (rewrite_start_codes)
						gf_bs_write_u32(dst_bs, 1);
					else
						gf_bs_write_int(dst_bs, copy_size, 8*nal_unit_size_field);

					gf_bs_write_u8(dst_bs, nal_hdr);
					gf_bs_write_data(dst_bs, buffer, copy_size-1);
				}

				gf_isom_sample_del(&ref_samp);
				ref_samp = NULL;
				gf_bs_del(ref_bs);
				ref_bs = NULL;
				gf_isom_set_nalu_extract_mode(file, ref_track_num, cur_extract_mode);
				break;
			default:
				//skip to end of this NALU
				gf_bs_skip_bytes(src_bs, nal_size-1);
				continue;
			}
		} else {
			gf_bs_read_data(src_bs, buffer, nal_size-1);
			if (rewrite_start_codes)
				gf_bs_write_u32(dst_bs, 1);
			else
				gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field);

			gf_bs_write_u8(dst_bs, nal_hdr);
			gf_bs_write_data(dst_bs, buffer, nal_size-1);
		}
	}