ref: 2ac355532dc6bd13696bb8b06be924ed1a097d23
dir: /common/mp4av/mpeg3.cpp/
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2002. All Rights Reserved.
*
* Contributor(s):
* Bill May (wmay@cisco.com)
*/
#include "mpeg4ip.h"
#include "mp4av.h"
//#define DEBUG_MPEG3_HINT 1
static double mpeg3_frame_rate_table[16] =
{
0.0, /* Pad */
24000.0/1001.0, /* Official frame rates */
24.0,
25.0,
30000.0/1001.0,
30.0,
50.0,
((60.0*1000.0)/1001.0),
60.0,
1, /* Unofficial economy rates */
5,
10,
12,
15,
0,
0,
};
#define MPEG3_START_CODE_PREFIX 0x000001
#define MPEG3_SEQUENCE_START_CODE 0x000001b3
#define MPEG3_PICTURE_START_CODE 0x00000100
#define MPEG3_GOP_START_CODE 0x000001b8
#define MPEG3_EXT_START_CODE 0x000001b5
#define MPEG3_SLICE_MIN_START 0x00000101
#define MPEG3_SLICE_MAX_START 0x000001af
#define SEQ_ID 1
extern "C" int MP4AV_Mpeg3ParseSeqHdr (uint8_t *pbuffer,
uint32_t buflen,
int *have_mpeg2,
uint32_t *height,
uint32_t *width,
double *frame_rate,
double *bitrate)
{
uint32_t framerate_code;
uint32_t bitrate_int;
uint32_t bitrate_ext;
#if 1
uint32_t scode, ix;
int found = -1;
*have_mpeg2 = 0;
buflen -= 6;
bitrate_int = 0;
for (ix = 0; ix < buflen; ix++, pbuffer++) {
scode = (pbuffer[0] << 24) | (pbuffer[1] << 16) | (pbuffer[2] << 8) |
pbuffer[3];
if (scode == MPEG3_SEQUENCE_START_CODE) {
pbuffer += sizeof(uint32_t);
*width = (pbuffer[0]);
*width <<= 4;
*width |= ((pbuffer[1] >> 4) &0xf);
*height = (pbuffer[1] & 0xf);
*height <<= 8;
*height |= pbuffer[2];
framerate_code = pbuffer[3] & 0xf;
*frame_rate = mpeg3_frame_rate_table[framerate_code];
// 18 bits
bitrate_int = (pbuffer[4] << 10) |
(pbuffer[5] << 2) |
((pbuffer[6] >> 6) & 0x3);
*bitrate = bitrate_int;
*bitrate *= 400.0;
ix += sizeof(uint32_t) + 7;
pbuffer += 7;
found = 0;
} else if (found == 0) {
if (scode == MPEG3_EXT_START_CODE) {
pbuffer += sizeof(uint32_t);
ix += sizeof(uint32_t);
switch ((pbuffer[0] >> 4) & 0xf) {
case SEQ_ID:
*have_mpeg2 = 1;
*height = ((pbuffer[1] & 0x1) << 13) |
((pbuffer[2] & 0x80) << 5) |
(*height & 0x0fff);
*width = (((pbuffer[2] >> 5) & 0x3) << 12) | (*width & 0x0fff);
bitrate_ext = (pbuffer[2] & 0x1f) << 7;
bitrate_ext |= (pbuffer[3] >> 1) & 0x7f;
bitrate_int |= (bitrate_ext << 18);
*bitrate = bitrate_int;
*bitrate *= 400.0;
break;
default:
break;
}
pbuffer++;
ix++;
} else if (scode == MPEG3_PICTURE_START_CODE) {
return found;
}
}
}
return found;
#else
// if you want to do the whole frame
int ix;
CBitstream bs(pbuffer, buflen);
try {
while (bs.PeekBits(32) != MPEG3_SEQUENCE_START_CODE) {
bs.GetBits(8);
}
bs.GetBits(32); // start code
*height = bs.GetBits(12);
*width = bs.GetBits(12);
bs.GetBits(4);
framerate_code = bs.GetBits(4);
*frame_rate = mpeg3_frame_rate_table[framerate_code];
bs.GetBits(18); // bitrate
bs.GetBits(1); // marker bit
bs.GetBits(10); // vbv buffer
bs.GetBits(1); // constrained params
if (bs.GetBits(1)) { // intra_quantizer_matrix
for (ix = 0; ix < 64; ix++) {
bs.GetBits(8);
}
}
if (bs.GetBits(1)) { // non_intra_quantizer_matrix
for (ix = 0; ix < 64; ix++) {
bs.GetBits(8);
}
}
} catch (...) {
return false;
}
return true;
#endif
}
extern "C" int MP4AV_Mpeg3PictHdrType (uint8_t *pbuffer)
{
pbuffer += sizeof(uint32_t);
return ((pbuffer[1] >> 3) & 0x7);
}
static int FindNextStart (uint8_t *pbuffer,
uint32_t buflen,
uint32_t *optr,
uint32_t *scode)
{
uint32_t value;
uint32_t offset;
for (offset = 0; offset < buflen; offset++, pbuffer++) {
value = (pbuffer[0] << 16) | (pbuffer[1] << 8) | (pbuffer[2] << 0);
if (value == MPEG3_START_CODE_PREFIX) {
*optr = offset;
*scode = (value << 8) | pbuffer[3];
return 0;
}
}
return -1;
}
static int FindNextSliceStart (uint8_t *pbuffer,
uint32_t startoffset,
uint32_t buflen,
uint32_t *slice_offset)
{
uint32_t slicestart, code;
while (FindNextStart(pbuffer + startoffset,
buflen - startoffset,
&slicestart,
&code) >= 0) {
#ifdef DEBUG_MPEG3_HINT
printf("Code %x at offset %d\n",
code, startoffset + slicestart);
#endif
if ((code >= MPEG3_SLICE_MIN_START) &&
(code <= MPEG3_SLICE_MAX_START)) {
*slice_offset = slicestart + startoffset;
return 0;
}
startoffset += slicestart + 4;
}
return -1;
}
extern "C" int MP4AV_Mpeg3FindGopOrPictHdr (uint8_t *pbuffer,
uint32_t buflen,
int *frame_type)
{
uint32_t value;
uint32_t offset;
int ftype;
for (offset = 0; offset < buflen; offset++, pbuffer++) {
value = (pbuffer[0] << 24) | (pbuffer[1] << 16) | (pbuffer[2] << 8) |
pbuffer[3];
if (value == MPEG3_PICTURE_START_CODE) {
ftype = MP4AV_Mpeg3PictHdrType(pbuffer);
if (frame_type != NULL) *frame_type = ftype;
if (ftype == 1) {
return 0;
} else {
return -1;
}
} else if (value == MPEG3_GOP_START_CODE) {
return 1;
}
}
return -1;
}
extern "C" bool Mpeg12Hinter (MP4FileHandle mp4file,
MP4TrackId trackid,
uint16_t maxPayloadSize)
{
uint32_t numSamples, maxSampleSize;
uint8_t videoType;
uint8_t rfc2250[4], rfc2250_2;
uint32_t offset;
uint32_t scode;
int have_seq;
bool stop;
uint8_t *buffer, *pbuffer;
uint8_t *pstart;
uint8_t type;
uint32_t next_slice, prev_slice;
bool slice_at_begin;
uint32_t sampleSize;
MP4SampleId sid;
MP4TrackId hintTrackId;
numSamples = MP4GetTrackNumberOfSamples(mp4file, trackid);
maxSampleSize = MP4GetTrackMaxSampleSize(mp4file, trackid);
if (numSamples == 0) return false;
videoType = MP4GetTrackVideoType(mp4file, trackid);
if (videoType != MP4_MPEG1_VIDEO_TYPE &&
videoType != MP4_MPEG2_VIDEO_TYPE) {
return false;
}
hintTrackId = MP4AddHintTrack(mp4file, trackid);
if (hintTrackId == MP4_INVALID_TRACK_ID) {
return false;
}
uint8_t payload = 32;
MP4SetHintTrackRtpPayload(mp4file, hintTrackId, "MPV", &payload, 0);
buffer = (uint8_t *)malloc(maxSampleSize);
if (buffer == NULL) {
MP4DeleteTrack(mp4file, hintTrackId);
return false;
}
maxPayloadSize -= 24; // this is for the 4 byte header
for (sid = 1; sid <= numSamples; sid++) {
sampleSize = maxSampleSize;
MP4Timestamp startTime;
MP4Duration duration;
MP4Duration renderingOffset;
bool isSyncSample;
bool rc = MP4ReadSample(mp4file, trackid, sid,
&buffer, &sampleSize,
&startTime, &duration,
&renderingOffset, &isSyncSample);
#ifdef DEBUG_MPEG3_HINT
printf("sid %d - sample size %d\n", sid, sampleSize);
#endif
if (rc == false) {
MP4DeleteTrack(mp4file, hintTrackId);
return false;
}
// need to add rfc2250 header
offset = 0;
have_seq = 0;
pbuffer = buffer;
stop = false;
do {
uint32_t oldoffset;
oldoffset = offset;
if (FindNextStart(pbuffer + offset,
sampleSize - offset,
&offset,
&scode) < 0) {
// didn't find the start code
#ifdef DEBUG_MPEG3_HINT
printf("didn't find start code\n");
#endif
stop = true;
} else {
offset += oldoffset;
#ifdef DEBUG_MPEG3_HINT
printf("next sscode %x found at %d\n", scode, offset);
#endif
if (scode == MPEG3_SEQUENCE_START_CODE) have_seq = 1;
offset += 4; // start with next value
}
} while (scode != MPEG3_PICTURE_START_CODE && stop == false);
pstart = pbuffer + offset; // point to inside of picture start
type = (pstart[1] >> 3) & 0x7;
rfc2250[0] = (*pstart >> 6) & 0x3;
rfc2250[1] = (pstart[0] << 2) | ((pstart[1] >> 6) & 0x3); // temporal ref
rfc2250[2] = type;
if (have_seq != 0) {
rfc2250[2] |= 0x20;
have_seq = 0;
}
rfc2250_2 = rfc2250[2];
rfc2250[3] = 0;
if (type == 2 || type == 3) {
rfc2250[3] = pstart[3] << 5;
if ((pstart[4] & 0x80) != 0) rfc2250[3] |= 0x10;
if (type == 3) {
rfc2250[3] |= (pstart[4] >> 3) & 0xf;
}
}
MP4AddRtpVideoHint(mp4file, hintTrackId, type == 3, renderingOffset);
// Find the next slice. Then we can add the header if the next
// slice will be in the start. This lets us set the S bit in
// rfc2250[2]. Then we need to loop to find the next slice that's
// not in the buffer size - this should be in the while loop.
prev_slice = 0;
if (FindNextSliceStart(pbuffer, offset, sampleSize, &next_slice) < 0) {
slice_at_begin = false;
} else {
slice_at_begin = true;
}
#ifdef DEBUG_MPEG3_HINT
printf("starting slice at %d\n", next_slice);
#endif
offset = 0;
bool nomoreslices = false;
bool found_slice = slice_at_begin;
bool onfirst = true;
while (sampleSize > 0) {
bool isLastPacket;
uint32_t len_to_write;
if (sampleSize <= maxPayloadSize) {
// leave started_slice alone
len_to_write = sampleSize;
isLastPacket = true;
prev_slice = 0;
} else {
found_slice = (onfirst == false) && (nomoreslices == false) && (next_slice <= maxPayloadSize);
onfirst = false;
isLastPacket = false;
while (nomoreslices == false && next_slice <= maxPayloadSize) {
prev_slice = next_slice;
if (FindNextSliceStart(pbuffer, next_slice + 4, sampleSize, &next_slice) >= 0) {
#ifdef DEBUG_MPEG3_HINT
printf("prev_slice %u next slice %u %u\n", prev_slice, next_slice,
offset + next_slice);
#endif
found_slice = true;
} else {
// at end
nomoreslices = true;
}
}
// prev_slice should have the end value. If it's not 0, we have
// the end of the slice.
if (found_slice) len_to_write = prev_slice;
else len_to_write = MIN(maxPayloadSize, sampleSize);
}
rfc2250[2] = rfc2250_2;
if (slice_at_begin) {
rfc2250[2] |= 0x10; // set b bit
}
if (found_slice || isLastPacket) {
rfc2250[2] |= 0x08; // set end of slice bit
slice_at_begin = true; // for next time
} else {
slice_at_begin = false;
}
#ifdef DEBUG_MPEG3_HINT
printf("Adding packet, sid %u prev slice %u len_to_write %u\n",
sid, prev_slice, len_to_write);
printf("Next slice %u offset %u %x %x %x %x\n\n",
next_slice, offset,
rfc2250[0], rfc2250[1], rfc2250[2], rfc2250[3]);
#endif
// okay - we can now write out this packet.
MP4AddRtpPacket(mp4file, hintTrackId, isLastPacket);
// add the 4 byte header
MP4AddRtpImmediateData(mp4file, hintTrackId, rfc2250, sizeof(rfc2250));
// add the immediate data
MP4AddRtpSampleData(mp4file, hintTrackId, sid, offset, len_to_write);
offset += len_to_write;
sampleSize -= len_to_write;
prev_slice = 0;
next_slice -= len_to_write;
pbuffer += len_to_write;
}
MP4WriteRtpHint(mp4file, hintTrackId, duration, type == 1);
}
free(buffer);
return true;
}