示例#1
0
GF_EXPORT
const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID)
{
	u32 i, count;
	GF_TrackBox *trak;
	GF_UserDataMap *map;
	GF_HintInfoBox *hinf;
	GF_PAYTBox *payt;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak || !index) return NULL;

	if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return NULL;
	map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL);
	if (!map) return NULL;
	if (gf_list_count(map->boxList) != 1) return NULL;

	hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0);
	count = 0;
	i = 0;
	while ((payt = gf_list_enum(hinf->boxList, &i))) {
		if (payt->type == GF_ISOM_BOX_TYPE_PAYT) {
			count++;
			if (count == index) {
				if (payID) *payID=payt->payloadCode;
				return payt->payloadString;
			}
		}
	}
	return NULL;
}
示例#2
0
//remove all SDP info at the track level
GF_Err gf_isom_sdp_clean_track(GF_ISOFile *the_file, u32 trackNumber)
{
	GF_TrackBox *trak;
	GF_UserDataMap *map;
	GF_HintTrackInfoBox *hnti;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak) return GF_BAD_PARAM;

	//currently, only RTP hinting supports SDP
	if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL);
	if (!map) return GF_ISOM_INVALID_FILE;

	//we should have only one HNTI in the UDTA
	if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE;

	hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0);
	if (!hnti->SDP) return GF_OK;
	//and free the SDP
	gf_free(((GF_SDPBox *)hnti->SDP)->sdpText);
	((GF_SDPBox *)hnti->SDP)->sdpText = NULL;
	return GF_OK;
}
示例#3
0
GF_EXPORT
u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber)
{
	u32 i, count;
	GF_TrackBox *trak;
	GF_UserDataMap *map;
	GF_HintInfoBox *hinf;
	GF_PAYTBox *payt;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak) return 0;

	if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return 0;
	map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL);
	if (!map) return 0;
	if (gf_list_count(map->boxList) != 1) return 0;

	hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0);
	count = 0;
	i = 0;
	while ((payt = gf_list_enum(hinf->boxList, &i))) {
		if (payt->type == GF_ISOM_BOX_TYPE_PAYT) count++;
	}
	return count;
}
示例#4
0
GF_Err gf_isom_rtp_packet_begin(GF_ISOFile *the_file, u32 trackNumber, 
						   s32 relativeTime, 
						   u8 PackingBit, 
						   u8 eXtensionBit, 
						   u8 MarkerBit, 
						   u8 PayloadType, 
						   u8 B_frame, 
						   u8 IsRepeatedPacket, 
						   u16 SequenceNumber)
{
	GF_TrackBox *trak;
	GF_HintSampleEntryBox *entry;
	GF_RTPPacket *pck;
	u32 dataRefIndex;
	GF_Err e;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->hint_sample) return GF_BAD_PARAM;

	pck = (GF_RTPPacket *) gf_isom_hint_pck_new(entry->hint_sample->HintType);

	pck->P_bit = PackingBit ? 1 : 0;
	pck->X_bit = eXtensionBit ? 1 : 0;
	pck->M_bit = MarkerBit ? 1 : 0;
	pck->payloadType = PayloadType;
	pck->SequenceNumber = SequenceNumber;
	pck->B_bit = B_frame ? 1 : 0;
	pck->R_bit = IsRepeatedPacket ? 1 : 0;
	pck->relativeTransTime = relativeTime;
	return gf_list_add(entry->hint_sample->packetTable, pck);
}
示例#5
0
//set the time offset of this packet. This enables packets to be placed in the hint track 
//in decoding order, but have their presentation time-stamp in the transmitted 
//packet be in a different order. Typically used for MPEG video with B-frames
GF_Err gf_isom_rtp_packet_set_offset(GF_ISOFile *the_file, u32 trackNumber, s32 timeOffset)
{
	GF_RTPOBox *rtpo;
	GF_TrackBox *trak;
	GF_HintSampleEntryBox *entry;
	GF_RTPPacket *pck;
	u32 dataRefIndex, i;
	GF_Err e;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->hint_sample) return GF_BAD_PARAM;

	pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, gf_list_count(entry->hint_sample->packetTable) - 1);
	if (!pck) return GF_BAD_PARAM;

	//look in the TLV
	i=0;
	while ((rtpo = (GF_RTPOBox *)gf_list_enum(pck->TLV, &i))) {
		if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) {
			rtpo->timeOffset = timeOffset;
			return GF_OK;
		}
	}
	//not found, add it
	rtpo = (GF_RTPOBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RTPO);
	rtpo->timeOffset = timeOffset;

	return gf_list_add(pck->TLV, rtpo);
}
示例#6
0
//sets the RTP SequenceNumber Offset that the server will add to the packets
//if not set, the server adds a random offset
GF_Err gf_isom_rtp_set_time_sequence_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset)
{
	GF_TrackBox *trak;
	GF_HintSampleEntryBox *hdesc;
	u32 i, count;
	GF_SeqOffHintEntryBox *ent;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	//OK, create a new HintSampleDesc
	hdesc = (GF_HintSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, HintDescriptionIndex - 1);
	count = gf_list_count(hdesc->HintDataTable);

	for (i=0; i< count; i++) {
		ent = (GF_SeqOffHintEntryBox *)gf_list_get(hdesc->HintDataTable, i);
		if (ent->type == GF_ISOM_BOX_TYPE_SNRO) {			
			ent->SeqOffset = SequenceNumberOffset;
			return GF_OK;
		}
	}
	//we have to create a new entry...
	ent = (GF_SeqOffHintEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SNRO);
	ent->SeqOffset = SequenceNumberOffset;
	return gf_list_add(hdesc->HintDataTable, ent);
}
示例#7
0
GF_Err gf_isom_rtp_packet_set_flags(GF_ISOFile *the_file, u32 trackNumber,
						   u8 PackingBit, 
						   u8 eXtensionBit, 
						   u8 MarkerBit, 
						   u8 disposable_packet, 
						   u8 IsRepeatedPacket)
{
	GF_TrackBox *trak;
	GF_HintSampleEntryBox *entry;
	GF_RTPPacket *pck;
	u32 dataRefIndex, ind;
	GF_Err e;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->hint_sample) return GF_BAD_PARAM;

	ind = gf_list_count(entry->hint_sample->packetTable);
	if (!ind) return GF_BAD_PARAM;
	pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, ind-1);

	pck->P_bit = PackingBit ? 1 : 0;
	pck->X_bit = eXtensionBit ? 1 : 0;
	pck->M_bit = MarkerBit ? 1 : 0;
	pck->B_bit = disposable_packet ? 1 : 0;
	pck->R_bit = IsRepeatedPacket ? 1 : 0;
	return GF_OK;
}
示例#8
0
M4Err M4H_RTP_NewPacket(M4File *the_file, u32 trackNumber, 
						   s32 relativeTime, 
						   u8 PackingBit, 
						   u8 eXtensionBit, 
						   u8 MarkerBit, 
						   u8 PayloadType, 
						   u8 B_frame, 
						   u8 IsRepeatedPacket, 
						   u16 SequenceNumber)
{
	TrackAtom *trak;
	HintSampleEntryAtom *entry;
	RTPPacket *pck;
	u32 dataRefIndex;
	M4Err e;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->w_sample) return M4BadParam;

	pck = (RTPPacket *) New_HintPacket(entry->w_sample->HintType);

	pck->P_bit = PackingBit ? 1 : 0;
	pck->X_bit = eXtensionBit ? 1 : 0;
	pck->M_bit = MarkerBit ? 1 : 0;
	pck->payloadType = PayloadType;
	pck->SequenceNumber = SequenceNumber;
	pck->B_bit = B_frame ? 1 : 0;
	pck->R_bit = IsRepeatedPacket ? 1 : 0;
	pck->relativeTransTime = relativeTime;
	return ChainAddEntry(entry->w_sample->packetTable, pck);
}
示例#9
0
//remove all SDP info at the track level
M4Err M4H_SDP_CleanTrack(M4File *the_file, u32 trackNumber)
{
	TrackAtom *trak;
	UserDataMap *map;
	HintTrackInfoAtom *hnti;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return M4BadParam;

	//currently, only RTP hinting supports SDP
	if (!CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	map = udta_getEntry(trak->udta, HintTrackInfoAtomType);
	if (!map) return M4InvalidMP4File;

	//we should have only one HNTI in the UDTA
	if (ChainGetCount(map->atomList) != 1) return M4InvalidMP4File;

	hnti = ChainGetEntry(map->atomList, 0);
	if (!hnti->SDP) return M4OK;
	//and free the SDP
	free(((SDPAtom *)hnti->SDP)->sdpText);
	((SDPAtom *)hnti->SDP)->sdpText = NULL;
	return M4OK;
}
示例#10
0
M4Err M4H_RTP_SetPacketFlags(M4File *the_file, u32 trackNumber,
						   u8 PackingBit, 
						   u8 eXtensionBit, 
						   u8 MarkerBit, 
						   u8 B_frame, 
						   u8 IsRepeatedPacket)
{
	TrackAtom *trak;
	HintSampleEntryAtom *entry;
	RTPPacket *pck;
	u32 dataRefIndex, ind;
	M4Err e;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->w_sample) return M4BadParam;

	ind = ChainGetCount(entry->w_sample->packetTable);
	if (!ind) return M4BadParam;
	pck = ChainGetEntry(entry->w_sample->packetTable, ind-1);

	pck->P_bit = PackingBit ? 1 : 0;
	pck->X_bit = eXtensionBit ? 1 : 0;
	pck->M_bit = MarkerBit ? 1 : 0;
	pck->B_bit = B_frame ? 1 : 0;
	pck->R_bit = IsRepeatedPacket ? 1 : 0;
	return M4OK;
}
示例#11
0
//set the time offset of this packet. This enables packets to be placed in the hint track 
//in decoding order, but have their presentation time-stamp in the transmitted 
//packet be in a different order. Typically used for MPEG video with B-frames
M4Err M4H_RTP_SetPacketTimeOffset(M4File *the_file, u32 trackNumber, s32 timeOffset)
{
	RtpoAtom *rtpo;
	TrackAtom *trak;
	HintSampleEntryAtom *entry;
	RTPPacket *pck;
	u32 dataRefIndex, i;
	M4Err e;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (SampleEntryAtom **) &entry, &dataRefIndex);
	if (e) return e;
	if (!entry->w_sample) return M4BadParam;

	pck = ChainGetEntry(entry->w_sample->packetTable, ChainGetCount(entry->w_sample->packetTable) - 1);
	if (!pck) return M4BadParam;

	//look in the TLV
	for (i=0; i<ChainGetCount(pck->TLV); i++) {
		rtpo = ChainGetEntry(pck->TLV, i);
		if (rtpo->type == rtpoAtomType) {
			rtpo->timeOffset = timeOffset;
			return M4OK;
		}
	}
	//not found, add it
	rtpo = (RtpoAtom *) CreateAtom(rtpoAtomType);
	rtpo->timeOffset = timeOffset;

	return ChainAddEntry(pck->TLV, rtpo);
}
示例#12
0
//sets the RTP SequenceNumber Offset that the server will add to the packets
//if not set, the server adds a random offset
M4Err M4H_RTP_SetSequenceNumberOffset(M4File *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset)
{
	TrackAtom *trak;
	HintSampleEntryAtom *hdesc;
	u32 i, count;
	SeqOffHintEntry *ent;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	//OK, create a new HintSampleDesc
	hdesc = ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, HintDescriptionIndex - 1);
	count = ChainGetCount(hdesc->HintDataTable);

	for (i=0; i< count; i++) {
		ent = ChainGetEntry(hdesc->HintDataTable, i);
		if (ent->type == snroHintEntryType) {			
			ent->SeqOffset = SequenceNumberOffset;
			return M4OK;
		}
	}
	//we have to create a new entry...
	ent = (SeqOffHintEntry *) CreateAtom(snroHintEntryType);
	ent->SeqOffset = SequenceNumberOffset;
	return ChainAddEntry(hdesc->HintDataTable, ent);
}
示例#13
0
//to use with internally supported protocols
GF_Err gf_isom_new_hint_description(GF_ISOFile *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex)
{
	GF_Err e;
	u32 drefIndex;
	GF_TrackBox *trak;
	GF_HintSampleEntryBox *hdesc;
	GF_RelyHintBox *relyA;

	e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE);
	if (e) return e;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	*HintDescriptionIndex = 0;
	if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM;

	//OK, create a new HintSampleDesc
	hdesc = (GF_HintSampleEntryBox *) gf_isom_box_new(GetHintFormat(trak));

	if (HintTrackVersion > 0) hdesc->HintTrackVersion = HintTrackVersion;
	if (LastCompatibleVersion > 0) hdesc->LastCompatibleVersion = LastCompatibleVersion;

	//create a data reference - WE ONLY DEAL WITH SELF-CONTAINED HINT TRACKS
	e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, NULL, NULL, &drefIndex);
	if (e) return e;
	hdesc->dataReferenceIndex = drefIndex;

	//add the entry to our table...
	e = stsd_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *) hdesc);
	if (e) return e;
	*HintDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList);

	//RTP needs a default timeScale... use the media one.
	if (CheckHintFormat(trak, GF_ISOM_HINT_RTP)) {
		e = gf_isom_rtp_set_timescale(the_file, trackNumber, *HintDescriptionIndex, trak->Media->mediaHeader->timeScale);
		if (e) return e;
	}
	if (!Rely) return GF_OK;

	//we need a rely box (common to all protocols)
	relyA = (GF_RelyHintBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RELY);
	if (Rely == 1) {
		relyA->prefered = 1;
	} else {
		relyA->required = 1;
	}
	return gf_list_add(hdesc->HintDataTable, relyA);
}
示例#14
0
//to use with internally supported protocols
M4Err M4H_NewHintDescription(M4File *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex)
{
	M4Err e;
	u32 drefIndex;
	TrackAtom *trak;
	HintSampleEntryAtom *hdesc;
	RelyHintEntry *relyA;

	M4Err stsd_AddAtom(SampleDescriptionAtom *ptr, Atom *a);

	trak = GetTrackFromFile(the_file, trackNumber);
	*HintDescriptionIndex = 0;
	if (!trak || !IsHintTrack(trak)) return M4BadParam;

	//OK, create a new HintSampleDesc
	hdesc = (HintSampleEntryAtom *) CreateAtom(GetHintFormat(trak));

	if (HintTrackVersion > 0) hdesc->HintTrackVersion = HintTrackVersion;
	if (LastCompatibleVersion > 0) hdesc->LastCompatibleVersion = LastCompatibleVersion;

	//create a data reference - WE ONLY DEAL WITH SELF-CONTAINED HINT TRACKS
	e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, NULL, NULL, &drefIndex);
	if (e) return e;
	hdesc->dataReferenceIndex = drefIndex;

	//add the entry to our table...
	e = stsd_AddAtom(trak->Media->information->sampleTable->SampleDescription, (Atom *) hdesc);
	if (e) return e;
	*HintDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList);

	//RTP needs a default timeScale... use the media one.
	if (CheckHintFormat(trak, M4_Hint_RTP)) {
		e = M4H_RTP_SetTimeScale(the_file, trackNumber, *HintDescriptionIndex, trak->Media->mediaHeader->timeScale);
		if (e) return e;
	}
	if (!Rely) return M4OK;

	//we need a rely atom (common to all protocols)
	relyA = (RelyHintEntry *) CreateAtom(relyHintEntryType);
	if (Rely == 1) {
		relyA->prefered = 1;
	} else {
		relyA->required = 1;
	}
	return ChainAddEntry(hdesc->HintDataTable, relyA);
}
示例#15
0
//add an SDP line to the SDP container at the track level (media-specific SDP info)
GF_EXPORT
GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text)
{
	GF_TrackBox *trak;
	GF_UserDataMap *map;
	GF_HintTrackInfoBox *hnti;
	GF_SDPBox *sdp;
	GF_Err e;
	char *buf;

	trak = gf_isom_get_track_from_file(the_file, trackNumber);
	if (!trak) return GF_BAD_PARAM;

	//currently, only RTP hinting supports SDP
	if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM;

	map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL);
	if (!map) return GF_ISOM_INVALID_FILE;

	//we should have only one HNTI in the UDTA
	if (gf_list_count(map->other_boxes) != 1) return GF_ISOM_INVALID_FILE;

	hnti = (GF_HintTrackInfoBox *)gf_list_get(map->other_boxes, 0);
	if (!hnti->SDP) {
		e = hnti_AddBox(hnti, gf_isom_box_new(GF_ISOM_BOX_TYPE_SDP));
		if (e) return e;
	}
	sdp = (GF_SDPBox *) hnti->SDP;

	if (!sdp->sdpText) {
		sdp->sdpText = (char *)gf_malloc(sizeof(char) * (strlen(text) + 3));
		strcpy(sdp->sdpText, text);
		strcat(sdp->sdpText, "\r\n");
		return GF_OK;
	}
	buf = (char *)gf_malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3));
	strcpy(buf, sdp->sdpText);
	strcat(buf, text);
	strcat(buf, "\r\n");
	gf_free(sdp->sdpText);
	ReorderSDP(buf, GF_FALSE);
	sdp->sdpText = buf;
	return GF_OK;
}
示例#16
0
//add an SDP line to the SDP container at the track level (media-specific SDP info)
M4Err M4H_SDP_TrackAddLine(M4File *the_file, u32 trackNumber, const char *text)
{
	TrackAtom *trak;
	UserDataMap *map;
	HintTrackInfoAtom *hnti;
	SDPAtom *sdp;
	M4Err e;
	char *buf;
	M4Err hnti_AddAtom(HintTrackInfoAtom *hnti, Atom *a);

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return M4BadParam;

	//currently, only RTP hinting supports SDP
	if (!CheckHintFormat(trak, M4_Hint_RTP)) return M4BadParam;

	map = udta_getEntry(trak->udta, HintTrackInfoAtomType);
	if (!map) return M4InvalidMP4File;

	//we should have only one HNTI in the UDTA
	if (ChainGetCount(map->atomList) != 1) return M4InvalidMP4File;

	hnti = ChainGetEntry(map->atomList, 0);
	if (!hnti->SDP) {
		e = hnti_AddAtom(hnti, CreateAtom(SDPAtomType));
		if (e) return e;
	}
	sdp = (SDPAtom *) hnti->SDP;

	if (!sdp->sdpText) {
		sdp->sdpText = malloc(sizeof(char) * (strlen(text) + 3));
		strcpy(sdp->sdpText, text);
		strcat(sdp->sdpText, "\r\n");
		return M4OK;
	}
	buf = malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3));
	strcpy(buf, sdp->sdpText);
	strcat(buf, text);
	strcat(buf, "\r\n");
	free(sdp->sdpText);
	sdp->sdpText = buf;	
	return M4OK;
}