blob: 20702925ff7f00e1315cef6727ec159e49f0711a [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2012
* All rights reserved
*
* This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/internal/ietf_dev.h>
#ifndef GPAC_DISABLE_STREAMING
#include <gpac/constants.h>
#include <gpac/avparse.h>
static void mpa12_do_flush(GP_RTPPacketizer *builder, Bool start_new)
{
u8 *tmp;
u32 tmp_size;
/*flush*/
if (builder->pck_hdr) {
gf_bs_get_content(builder->pck_hdr, &tmp, &tmp_size);
builder->OnData(builder->cbk_obj, tmp, tmp_size, GF_TRUE);
gf_free(tmp);
if (gf_bs_get_size(builder->payload)) {
gf_bs_get_content(builder->payload, &tmp, &tmp_size);
builder->OnData(builder->cbk_obj, tmp, tmp_size, GF_FALSE);
gf_free(tmp);
}
builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header);
gf_bs_del(builder->pck_hdr);
gf_bs_del(builder->payload);
builder->pck_hdr = NULL;
builder->payload = NULL;
builder->bytesInPacket = 0;
}
if (!start_new) return;
builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp;
builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
builder->payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
/*create new RTP Packet */
builder->rtp_header.SequenceNumber += 1;
builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header);
builder->first_sl_in_rtp = GF_TRUE;
builder->bytesInPacket = 0;
}
GF_Err gp_rtp_builder_do_mpeg12_audio(GP_RTPPacketizer *builder, u8 *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize)
{
u32 pck_size;
u16 offset;
/*if no data flush, if nothing start if not enough space restart*/
if (!data || !builder->bytesInPacket || (builder->bytesInPacket + data_size > builder->Path_MTU)) {
mpa12_do_flush(builder, data ? GF_TRUE : GF_FALSE);
if (!data) return GF_OK;
}
offset = 0;
while (data_size) {
if (data_size + 4 < builder->Path_MTU) {
pck_size = data_size;
} else {
pck_size = builder->Path_MTU - 4;
}
if (builder->first_sl_in_rtp) {
gf_bs_write_u16(builder->pck_hdr, 0);
gf_bs_write_u16(builder->pck_hdr, offset);
builder->first_sl_in_rtp = GF_FALSE;
builder->bytesInPacket = 2;
}
/*add reference*/
if (builder->OnDataReference)
builder->OnDataReference(builder->cbk_obj, pck_size, offset);
else
gf_bs_write_data(builder->payload, data + offset, pck_size);
data_size -= pck_size;
builder->bytesInPacket += pck_size;
/*start new packet if fragmenting*/
if (data_size) {
offset += (u16) pck_size;
mpa12_do_flush(builder, GF_TRUE);
}
}
/*if offset or no aggregation*/
if (offset || !(builder->flags & GP_RTP_PCK_USE_MULTI) ) mpa12_do_flush(builder, GF_FALSE);
return GF_OK;
}
#ifndef GPAC_DISABLE_AV_PARSERS
#define MPEG12_PICTURE_START_CODE 0x00000100
#define MPEG12_SEQUENCE_START_CODE 0x000001b3
GF_Err gp_rtp_builder_do_mpeg12_video(GP_RTPPacketizer *builder, u8 *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize)
{
u32 startcode, pic_type, max_pck_size, offset, prev_slice, next_slice;
Bool start_with_slice, slices_done, got_slice, first_slice, have_seq;
u8 mpv_hdr[4];
u8 *payload, *buffer;
/*no flsuh (no aggregation)*/
if (!data) return GF_OK;
if (data_size<4) return GF_NON_COMPLIANT_BITSTREAM;
offset = 0;
have_seq = GF_FALSE;
while (1) {
u32 oldoffset;
oldoffset = offset;
if (gf_mv12_next_start_code((unsigned char *) data + offset, data_size - offset, &offset, &startcode) < 0)
break;
offset += oldoffset;
if (startcode == MPEG12_SEQUENCE_START_CODE) have_seq = GF_TRUE;
offset += 4;
if (startcode == MPEG12_PICTURE_START_CODE) break;
}
max_pck_size = builder->Path_MTU - 4;
if (data_size <= offset + 1) return GF_NON_COMPLIANT_BITSTREAM;
payload = data + offset;
pic_type = (payload[1] >> 3) & 0x7;
/*first 6 bits (MBZ and T bit) not used*/
/*temp ref on 10 bits*/
mpv_hdr[0] = (payload[0] >> 6) & 0x3;
mpv_hdr[1] = (payload[0] << 2) | ((payload[1] >> 6) & 0x3);
mpv_hdr[2] = pic_type;
mpv_hdr[3] = 0;
if ((pic_type==2) || (pic_type== 3)) {
if (data_size <= offset + 4) return GF_NON_COMPLIANT_BITSTREAM;
mpv_hdr[3] = (u8) ((((u32)payload[3]) << 5) & 0xf);
if ((payload[4] & 0x80) != 0) mpv_hdr[3] |= 0x10;
if (pic_type == 3) mpv_hdr[3] |= (payload[4] >> 3) & 0xf;
}
/*start packet*/
builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp;
builder->rtp_header.Marker = 1;
builder->rtp_header.SequenceNumber += 1;
builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header);
buffer = data;
prev_slice = 0;
start_with_slice = (gf_mv12_next_slice_start((unsigned char *)buffer, offset, data_size, &next_slice) >= 0) ? GF_TRUE : GF_FALSE;
offset = 0;
slices_done = GF_FALSE;
got_slice = start_with_slice;
first_slice = GF_TRUE;
while (data_size > 0) {
Bool last_pck;
u32 len_to_write;
if (data_size <= max_pck_size) {
len_to_write = data_size;
last_pck = GF_TRUE;
// prev_slice = 0;
} else {
got_slice = (!first_slice && !slices_done && (next_slice <= max_pck_size)) ? GF_TRUE : GF_FALSE;
first_slice = GF_FALSE;
last_pck = GF_FALSE;
while (!slices_done && (next_slice <= max_pck_size)) {
prev_slice = next_slice;
if (gf_mv12_next_slice_start((unsigned char *)buffer, next_slice + 4, data_size, &next_slice) >= 0) {
got_slice = GF_TRUE;
} else {
slices_done = GF_TRUE;
}
}
if (got_slice) len_to_write = prev_slice;
else len_to_write = MIN(max_pck_size, data_size);
}
mpv_hdr[2] = pic_type;
if (have_seq) {
mpv_hdr[2] |= 0x20;
have_seq = GF_FALSE;
}
if (first_slice) mpv_hdr[2] |= 0x10;
if (got_slice || last_pck) {
mpv_hdr[2] |= 0x08;
// start_with_slice = GF_TRUE;
} else {
// start_with_slice = GF_FALSE;
}
builder->OnData(builder->cbk_obj, mpv_hdr, 4, GF_FALSE);
if (builder->OnDataReference) {
builder->OnDataReference(builder->cbk_obj, len_to_write, offset);
} else {
builder->OnData(builder->cbk_obj, data + offset, len_to_write, GF_FALSE);
}
builder->rtp_header.Marker = last_pck ? 1 : 0;
builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header);
offset += len_to_write;
data_size -= len_to_write;
prev_slice = 0;
next_slice -= len_to_write;
buffer += len_to_write;
if (!last_pck) {
builder->rtp_header.Marker = 0;
builder->rtp_header.SequenceNumber += 1;
builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header);
}
}
return GF_OK;
}
#endif
#endif /*GPAC_DISABLE_STREAMING*/