| /* | |
| * 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*/ |