mirror of
https://github.com/panzi/mediaextract
synced 2025-02-19 05:56:51 +00:00
added mpeg ts support
This commit is contained in:
parent
41d9aaf905
commit
f6db42aa32
@ -122,6 +122,7 @@ files.
|
||||
mpg123 MPEG layer 1/2/3 files (MP1, MP2, MP3)
|
||||
mpeg1 MPEG 1 System Streams
|
||||
mpegps MPEG 2 Program Streams
|
||||
mpegts MPEG 2 Transport Streams
|
||||
mp4 MP4 files (M4A, M4V, 3GPP etc.)
|
||||
ogg Ogg files (Vorbis, Opus, Theora, etc.)
|
||||
png Portable Network Graphics files
|
||||
@ -136,8 +137,9 @@ files.
|
||||
MP3 files usually have an ID3v2 tag at the start, so using the
|
||||
'id3v2' format is the better option anyway.
|
||||
|
||||
The detection accuracy of MOD files is not much better and thus
|
||||
the 'mpg123' and 'mod' formats are per default disabled.
|
||||
The detection accuracy of MOD files is not much better and of MPEG TS
|
||||
it is even worse and thus the 'mpg123', 'mpegts' and 'mod' formats
|
||||
are per default disabled.
|
||||
|
||||
NOTE: When using only the 'mpg123' format but not 'id3v2' any ID3v2
|
||||
tag will be stripped. ID3v1 tags will still be kept.
|
||||
|
@ -155,6 +155,7 @@ static int usage(int argc, char **argv)
|
||||
" mpg123 MPEG layer 1/2/3 files (MP1, MP2, MP3)\n"
|
||||
" mpeg1 MPEG 1 System Streams\n"
|
||||
" mpegps MPEG 2 Program Streams\n"
|
||||
" mpegts MPEG 2 Transport Streams\n"
|
||||
" mp4 MP4 files (M4A, M4V, 3GPP etc.)\n"
|
||||
" ogg Ogg files (Vorbis, Opus, Theora, etc.)\n"
|
||||
" png Portable Network Graphics files\n"
|
||||
@ -169,8 +170,9 @@ static int usage(int argc, char **argv)
|
||||
" MP3 files usually have an ID3v2 tag at the start, so using the\n"
|
||||
" 'id3v2' format is the better option anyway.\n"
|
||||
"\n"
|
||||
" The detection accuracy of MOD files is not much better and thus\n"
|
||||
" the 'mpg123' and 'mod' formats are per default disabled.\n"
|
||||
" The detection accuracy of MOD files is not much better and of MPEG TS\n"
|
||||
" it is even worse and thus the 'mpg123', 'mpegts' and 'mod' formats\n"
|
||||
" are per default disabled.\n"
|
||||
"\n"
|
||||
" NOTE: When using only the 'mpg123' format but not 'id3v2' any ID3v2\n"
|
||||
" tag will be stripped. ID3v1 tags will still be kept.\n"
|
||||
@ -442,7 +444,14 @@ int do_extract(const uint8_t *filedata, size_t filesize, const struct extract_op
|
||||
continue;
|
||||
}
|
||||
|
||||
if (formats & (MPEG1 | MPEGPS | MPEGVS | MPEGTS) && IS_MPEG_MAGIC(magic) && mpeg_isfile(ptr, input_len, formats, &length))
|
||||
if (formats & (MPEG1 | MPEGPS | MPEGVS) && IS_MPEG_MAGIC(magic) && mpeg_isfile(ptr, input_len, formats, &length))
|
||||
{
|
||||
WRITE_FILE(ptr, length, "mpg");
|
||||
ptr += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (formats & MPEGTS && IS_MPEG_TS_MAGIC(ptr) && mpeg_isfile(ptr, input_len, formats, &length))
|
||||
{
|
||||
WRITE_FILE(ptr, length, "mpg");
|
||||
ptr += length;
|
||||
|
224
src/mpeg.c
Normal file
224
src/mpeg.c
Normal file
@ -0,0 +1,224 @@
|
||||
#include "mpeg.h"
|
||||
|
||||
// See: http://www.andrewduncan.ws/MPEG/MPEG-2_Picts.html
|
||||
// http://lostcode.wikidot.com/transportstream
|
||||
// http://en.wikipedia.org/wiki/MPEG_transport_stream
|
||||
// http://dmr.ath.cx/notes/mpeg1.html
|
||||
|
||||
static size_t mpeg_ispacket(const uint8_t *data, size_t input_len)
|
||||
{
|
||||
if (input_len < 6)
|
||||
return 0;
|
||||
|
||||
uint32_t magic = MAGIC(data);
|
||||
if (magic < MPEG_PKG_MIN_MAGIC || magic > MPEG_PKG_MAX_MAGIC)
|
||||
return 0;
|
||||
|
||||
size_t length = 6 + ((data[4] << 8) | data[5]);
|
||||
|
||||
if (length <= 6 || length > input_len)
|
||||
return 0;
|
||||
|
||||
// truncated file?
|
||||
if (length > input_len)
|
||||
return input_len;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static size_t mpeg_ispack(const uint8_t *data, size_t input_len, enum fileformat *format)
|
||||
{
|
||||
if (input_len < 12 || MAGIC(data) != MPEG_MAGIC)
|
||||
return 0;
|
||||
|
||||
size_t length = 12;
|
||||
|
||||
switch (data[4] >> 6)
|
||||
{
|
||||
case 0: // MPEG 1
|
||||
*format = MPEG1;
|
||||
break;
|
||||
|
||||
case 1: // MPEG 2
|
||||
if (input_len < 14)
|
||||
return 0;
|
||||
|
||||
// stuffing
|
||||
length = 14 + (data[13] & 0x3);
|
||||
|
||||
if (length > input_len)
|
||||
return 0;
|
||||
|
||||
if (!(data[4] & 0x4) || !(data[ 6] & 0x4) || !(data[8] & 0x4) ||
|
||||
!(data[9] & 0x1) || !(data[12] & 0x3))
|
||||
return 0;
|
||||
*format = MPEGPS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t *ptr = data + length;
|
||||
const uint8_t *end = data + input_len - 6;
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
size_t pkglen = mpeg_ispacket(ptr, input_len - length);
|
||||
|
||||
if (pkglen == 0)
|
||||
break;
|
||||
|
||||
length += pkglen;
|
||||
ptr += pkglen;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static size_t mpeg_ispacks(const uint8_t *data, size_t input_len, int formats)
|
||||
{
|
||||
enum fileformat format = NONE;
|
||||
size_t length = mpeg_ispack(data, input_len, &format);
|
||||
|
||||
if (length == 0 || (format & formats) == 0)
|
||||
return 0;
|
||||
|
||||
while (length < input_len)
|
||||
{
|
||||
// ignore up to 64 nil bytes
|
||||
size_t maxskip = input_len - length;
|
||||
if (maxskip > 66) maxskip = length + 66;
|
||||
else maxskip += length;
|
||||
|
||||
size_t i = length;
|
||||
while (i < maxskip && data[i] == 0)
|
||||
++ i;
|
||||
|
||||
// there are 2 nil bytes at the start of the magic
|
||||
if ((i - length) < 2)
|
||||
break;
|
||||
length = i - 2;
|
||||
|
||||
enum fileformat nextformat = NONE;
|
||||
size_t nextlen = mpeg_ispack(data + length, input_len - length, &nextformat);
|
||||
|
||||
if (nextlen == 0 || nextformat != format)
|
||||
break;
|
||||
|
||||
length += nextlen;
|
||||
}
|
||||
|
||||
if (length <= input_len - 4 && MAGIC(data + length) == MPEG_END_MAGIC)
|
||||
length += 4;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO: find out how to determine the sizes of all the parts of a MPEG VS
|
||||
static size_t mpeg_isvshdr(const uint8_t *data, size_t input_len)
|
||||
{
|
||||
if (input_len < 12 || MAGIC(data) != MPEG_VS_HDR_MAGIC)
|
||||
return 0;
|
||||
|
||||
size_t width = (data[4] << 4) | (data[5] >> 4);
|
||||
size_t height = (data[5] << 4) | data[6];
|
||||
size_t bitrate = (data[8] << 10) | (data[9] << 2) | (data[10] >> 6);
|
||||
|
||||
if (!width || !height || !bitrate || !(data[10] & 0x20))
|
||||
return 0;
|
||||
|
||||
if (data[11] & 0x2)
|
||||
{
|
||||
if (input_len < 76)
|
||||
return 0;
|
||||
|
||||
if (data[75] & 0x1)
|
||||
{
|
||||
if (input_len < 140)
|
||||
return 0;
|
||||
|
||||
return 140;
|
||||
}
|
||||
|
||||
return 76;
|
||||
}
|
||||
|
||||
return 12;
|
||||
}
|
||||
|
||||
static size_t mpeg_isvs(const uint8_t *data, size_t input_len)
|
||||
{
|
||||
size_t length = mpeg_isvshdr(data, input_length);
|
||||
|
||||
if (!length)
|
||||
return 0;
|
||||
|
||||
// TODO
|
||||
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t mpeg_istspack(const uint8_t *data, size_t input_len)
|
||||
{
|
||||
// There are versions with 192 and 208 bytes that prepend/append
|
||||
// a 4 byte timecode or 20 byte for error correction. Because
|
||||
// I don't have any example files and don't know how to detect
|
||||
// these variations I haven't implemented them for now. See:
|
||||
// http://lostcode.wikidot.com/transportstream
|
||||
if (input_len < 188 || data[0] != MPEG_TS_SYNC_BYTE || !(data[3] & 0x30))
|
||||
return 0;
|
||||
|
||||
if (data[3] & 0x20)
|
||||
{
|
||||
size_t hdrlen = data[4];
|
||||
|
||||
if (hdrlen == 0 || hdrlen > 183)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 188;
|
||||
}
|
||||
|
||||
static size_t mpeg_ists(const uint8_t *data, size_t input_len)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
while (length < input_len)
|
||||
{
|
||||
size_t pkglen = mpeg_istspack(data + length, input_len - length);
|
||||
|
||||
if (!pkglen)
|
||||
break;
|
||||
|
||||
length += pkglen;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int mpeg_isfile(const uint8_t *data, size_t input_len, int formats, size_t *lengthptr)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
if (formats & (MPEG1 | MPEGPS))
|
||||
length = mpeg_ispacks(data, input_len, formats);
|
||||
|
||||
// if (!length && formats & MPEGVS)
|
||||
// length = mpeg_isvs(data, input_len);
|
||||
|
||||
if (!length && formats & MPEGTS)
|
||||
length = mpeg_ists(data, input_len);
|
||||
|
||||
if (length)
|
||||
{
|
||||
if (lengthptr)
|
||||
*lengthptr = length;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
26
src/mpeg.h
Normal file
26
src/mpeg.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef MEDIAEXTRACT_MPEG2_H__
|
||||
#define MEDIAEXTRACT_MPEG2_H__
|
||||
|
||||
#include "mediaextract.h"
|
||||
|
||||
#define MPEG_MAGIC MAGIC("\0\0\x01\xBA")
|
||||
#define MPEG_SYSHDR_MAGIC MAGIC("\0\0\x01\xBB")
|
||||
#define MPEG_PKG_MIN_MAGIC MAGIC("\x00\x00\x01\xBB")
|
||||
#define MPEG_PKG_MAX_MAGIC MAGIC("\x00\x00\x01\xFF")
|
||||
#define MPEG_END_MAGIC MAGIC("\0\0\x01\xB9")
|
||||
|
||||
#define MPEG_VS_HDR_MAGIC MAGIC("\0\0\x01\xB3")
|
||||
#define MPEG_VS_EXT_MAGIC MAGIC("\0\0\x01\xB5")
|
||||
#define MPEG_VS_GRP_MAGIC MAGIC("\0\0\x01\xB8")
|
||||
#define MPEG_VS_PCT_MAGIC MAGIC("\0\0\x01\x00")
|
||||
#define MPEG_VS_END_MAGIC MAGIC("\0\0\x01\xB7")
|
||||
|
||||
#define MPEG_VS_MAGIC MPEG_VS_HDR_MAGIC
|
||||
#define MPEG_TS_SYNC_BYTE 0x47
|
||||
|
||||
#define IS_MPEG_MAGIC(magic) ((magic) == MPEG_MAGIC)
|
||||
#define IS_MPEG_TS_MAGIC(ptr) ((ptr)[0] == MPEG_TS_SYNC_BYTE && (ptr)[3] & 0x30)
|
||||
|
||||
int mpeg_isfile(const uint8_t *data, size_t input_len, int formats, size_t *lengthptr);
|
||||
|
||||
#endif /* MEDIAEXTRACT_MPEG2_H__ */
|
Loading…
Reference in New Issue
Block a user