Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
TrueDei
FFmpeg
提交
f61dee2f
F
FFmpeg
项目概览
TrueDei
/
FFmpeg
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
FFmpeg
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f61dee2f
编写于
1月 23, 2011
作者:
P
Peter Ross
提交者:
Ronald S. Bultje
2月 03, 2011
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
wtv: filesystem implementation
Signed-off-by:
N
Ronald S. Bultje
<
rsbultje@gmail.com
>
上级
a9d921cb
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
564 addition
and
78 deletion
+564
-78
libavformat/wtv.c
libavformat/wtv.c
+564
-78
未找到文件。
libavformat/wtv.c
浏览文件 @
f61dee2f
/*
* Windows Television (WTV) demuxer
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
* Copyright (c) 2010
-2011
Peter Ross <pross@xvid.org>
*
* This file is part of FFmpeg.
*
...
...
@@ -26,6 +26,7 @@
*/
#include "libavutil/intreadwrite.h"
#include "libavutil/intfloat_readwrite.h"
#include "avformat.h"
#include "riff.h"
#include "asf.h"
...
...
@@ -37,22 +38,294 @@
#define ARG_GUID(g) \
g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15]
#define PRI_PRETTY_GUID \
"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x"
#define ARG_PRETTY_GUID(g) \
AV_RL32(g),AV_RL16(g+4),AV_RL16(g+6),g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15]
#define LEN_PRETTY_GUID 34
/*
*
* File system routines
*
*/
#define WTV_SECTOR_BITS 12
#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS)
#define WTV_BIGSECTOR_BITS 18
typedef
struct
{
int
seen_data
;
}
WtvStream
;
ByteIOContext
*
pb_filesystem
;
/** file system (AVFormatContext->pb) */
typedef
struct
WtvContext
{
uint64_t
pts
;
}
WtvContext
;
int
sector_bits
;
/** sector shift bits; used to convert sector number into pb_filesystem offset */
uint32_t
*
sectors
;
/** file allocation table */
int
nb_sectors
;
/** number of sectors */
int
error
;
int64_t
position
;
int64_t
length
;
}
WtvFile
;
/**
* @return bytes read, 0 on end of file, or <0 on error
*/
static
int
wtvfile_read_packet
(
void
*
opaque
,
uint8_t
*
buf
,
int
buf_size
)
{
WtvFile
*
wf
=
opaque
;
ByteIOContext
*
pb
=
wf
->
pb_filesystem
;
int
nread
=
0
;
if
(
wf
->
error
||
url_ferror
(
pb
))
return
-
1
;
if
(
wf
->
position
>=
wf
->
length
||
url_feof
(
pb
))
return
0
;
buf_size
=
FFMIN
(
buf_size
,
wf
->
length
-
wf
->
position
);
while
(
nread
<
buf_size
)
{
int
n
;
int
remaining_in_sector
=
(
1
<<
wf
->
sector_bits
)
-
(
wf
->
position
&
((
1
<<
wf
->
sector_bits
)
-
1
));
int
read_request
=
FFMIN
(
buf_size
-
nread
,
remaining_in_sector
);
n
=
get_buffer
(
pb
,
buf
,
read_request
);
if
(
n
<=
0
)
break
;
nread
+=
n
;
buf
+=
n
;
wf
->
position
+=
n
;
if
(
n
==
remaining_in_sector
)
{
int
i
=
wf
->
position
>>
wf
->
sector_bits
;
if
(
i
>=
wf
->
nb_sectors
||
(
wf
->
sectors
[
i
]
!=
wf
->
sectors
[
i
-
1
]
+
(
1
<<
(
wf
->
sector_bits
-
WTV_SECTOR_BITS
))
&&
url_fseek
(
pb
,
(
int64_t
)
wf
->
sectors
[
i
]
<<
WTV_SECTOR_BITS
,
SEEK_SET
)
<
0
))
{
wf
->
error
=
1
;
break
;
}
}
}
return
nread
;
}
/**
* @return position (or file length)
*/
static
int64_t
wtvfile_seek
(
void
*
opaque
,
int64_t
offset
,
int
whence
)
{
WtvFile
*
wf
=
opaque
;
ByteIOContext
*
pb
=
wf
->
pb_filesystem
;
if
(
whence
==
AVSEEK_SIZE
)
return
wf
->
length
;
else
if
(
whence
==
SEEK_CUR
)
offset
=
wf
->
position
+
offset
;
else
if
(
whence
==
SEEK_END
)
offset
=
wf
->
length
;
wf
->
error
=
offset
<
0
||
offset
>=
wf
->
length
||
url_fseek
(
pb
,
((
int64_t
)
wf
->
sectors
[
offset
>>
wf
->
sector_bits
]
<<
WTV_SECTOR_BITS
)
+
(
offset
&
((
1
<<
wf
->
sector_bits
)
-
1
)),
SEEK_SET
)
<
0
;
wf
->
position
=
offset
;
return
offset
;
}
/**
* read non-zero integers (le32) from input stream
* @param pb
* @param[out] data destination
* @param count maximum number of integers to read
* @return total number of integers read
*/
static
int
read_ints
(
ByteIOContext
*
pb
,
uint32_t
*
data
,
int
count
)
{
int
i
,
total
=
0
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
((
data
[
total
]
=
get_le32
(
pb
)))
total
++
;
}
return
total
;
}
/**
* Open file
* @param first_sector First sector
* @param length Length of file (bytes)
* @param depth File allocation table depth
* @return NULL on error
*/
static
ByteIOContext
*
wtvfile_open_sector
(
int
first_sector
,
uint64_t
length
,
int
depth
,
AVFormatContext
*
s
)
{
ByteIOContext
*
pb
;
WtvFile
*
wf
;
uint8_t
*
buffer
;
if
(
url_fseek
(
s
->
pb
,
first_sector
<<
WTV_SECTOR_BITS
,
SEEK_SET
)
<
0
)
return
NULL
;
wf
=
av_mallocz
(
sizeof
(
WtvFile
));
if
(
!
wf
)
return
NULL
;
if
(
depth
==
0
)
{
wf
->
sectors
=
av_malloc
(
sizeof
(
uint32_t
));
if
(
!
wf
->
sectors
)
{
av_free
(
wf
);
return
NULL
;
}
wf
->
sectors
[
0
]
=
first_sector
;
wf
->
nb_sectors
=
1
;
wf
->
sector_bits
=
WTV_SECTOR_BITS
;
}
else
if
(
depth
==
1
)
{
wf
->
sectors
=
av_malloc
(
WTV_SECTOR_SIZE
);
if
(
!
wf
->
sectors
)
{
av_free
(
wf
);
return
NULL
;
}
wf
->
nb_sectors
=
read_ints
(
s
->
pb
,
wf
->
sectors
,
WTV_SECTOR_SIZE
/
4
);
wf
->
sector_bits
=
length
&
(
1ULL
<<
63
)
?
WTV_SECTOR_BITS
:
WTV_BIGSECTOR_BITS
;
}
else
if
(
depth
==
2
)
{
uint32_t
sectors1
[
WTV_SECTOR_SIZE
/
4
];
int
nb_sectors1
=
read_ints
(
s
->
pb
,
sectors1
,
WTV_SECTOR_SIZE
/
4
);
int
i
;
wf
->
sectors
=
av_malloc
(
nb_sectors1
<<
WTV_SECTOR_BITS
);
if
(
!
wf
->
sectors
)
{
av_free
(
wf
);
return
NULL
;
}
wf
->
nb_sectors
=
0
;
for
(
i
=
0
;
i
<
nb_sectors1
;
i
++
)
{
if
(
url_fseek
(
s
->
pb
,
(
int64_t
)
sectors1
[
i
]
<<
WTV_SECTOR_BITS
,
SEEK_SET
)
<
0
)
break
;
wf
->
nb_sectors
+=
read_ints
(
s
->
pb
,
wf
->
sectors
+
i
*
WTV_SECTOR_SIZE
/
4
,
WTV_SECTOR_SIZE
/
4
);
}
wf
->
sector_bits
=
length
&
(
1ULL
<<
63
)
?
WTV_SECTOR_BITS
:
WTV_BIGSECTOR_BITS
;
}
else
{
av_log
(
s
,
AV_LOG_ERROR
,
"unsupported file allocation table depth (0x%x)
\n
"
,
depth
);
av_free
(
wf
);
return
NULL
;
}
if
(
!
wf
->
nb_sectors
)
{
av_free
(
wf
->
sectors
);
av_free
(
wf
);
return
NULL
;
}
/* check length */
length
&=
0xFFFFFFFFFFFF
;
if
(
length
>
((
int64_t
)
wf
->
nb_sectors
<<
wf
->
sector_bits
))
{
av_log
(
s
,
AV_LOG_WARNING
,
"reported file length (0x%"
PRIx64
") exceeds number of available sectors (0x%"
PRIx64
")
\n
"
,
length
,
(
int64_t
)
wf
->
nb_sectors
<<
wf
->
sector_bits
);
length
=
(
int64_t
)
wf
->
nb_sectors
<<
wf
->
sector_bits
;
}
wf
->
length
=
length
;
/* seek to intial sector */
wf
->
position
=
0
;
if
(
url_fseek
(
s
->
pb
,
(
int64_t
)
wf
->
sectors
[
0
]
<<
WTV_SECTOR_BITS
,
SEEK_SET
)
<
0
)
{
av_free
(
wf
->
sectors
);
av_free
(
wf
);
return
NULL
;
}
wf
->
pb_filesystem
=
s
->
pb
;
buffer
=
av_malloc
(
1
<<
wf
->
sector_bits
);
if
(
!
buffer
)
{
av_free
(
wf
->
sectors
);
av_free
(
wf
);
return
NULL
;
}
static
int
is_zero
(
uint8_t
*
v
,
int
n
)
pb
=
av_alloc_put_byte
(
buffer
,
1
<<
wf
->
sector_bits
,
0
,
wf
,
wtvfile_read_packet
,
NULL
,
wtvfile_seek
);
if
(
!
pb
)
{
av_free
(
buffer
);
av_free
(
wf
->
sectors
);
av_free
(
wf
);
}
return
pb
;
}
static
const
ff_asf_guid
dir_entry_guid
=
{
0x92
,
0xB7
,
0x74
,
0x91
,
0x59
,
0x70
,
0x70
,
0x44
,
0x88
,
0xDF
,
0x06
,
0x3B
,
0x82
,
0xCC
,
0x21
,
0x3D
};
/**
* Open file using filename
* @param[in] buf directory buffer
* @param buf_size directory buffer size
* @param[in] filename
* @param filename_size size of filename
* @return NULL on error
*/
static
ByteIOContext
*
wtvfile_open2
(
AVFormatContext
*
s
,
const
uint8_t
*
buf
,
int
buf_size
,
const
uint8_t
*
filename
,
int
filename_size
)
{
int
i
;
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
v
[
i
])
return
0
;
return
1
;
const
uint8_t
*
buf_end
=
buf
+
buf_size
;
while
(
buf
+
48
<=
buf_end
)
{
int
dir_length
,
name_size
,
first_sector
,
depth
;
uint64_t
file_length
;
const
uint8_t
*
name
;
if
(
ff_guidcmp
(
buf
,
dir_entry_guid
))
{
av_log
(
s
,
AV_LOG_ERROR
,
"unknown guid "
PRI_GUID
", expected dir_entry_guid; "
"remaining directory entries ignored
\n
"
,
ARG_GUID
(
buf
));
break
;
}
dir_length
=
AV_RL16
(
buf
+
16
);
file_length
=
AV_RL64
(
buf
+
24
);
name_size
=
2
*
AV_RL32
(
buf
+
32
);
if
(
buf
+
48
+
name_size
>
buf_end
)
{
av_log
(
s
,
AV_LOG_ERROR
,
"filename exceeds buffer size; remaining directory entries ignored
\n
"
);
break
;
}
first_sector
=
AV_RL32
(
buf
+
40
+
name_size
);
depth
=
AV_RL32
(
buf
+
44
+
name_size
);
/* compare file name; test optional null terminator */
name
=
buf
+
40
;
if
(
name_size
>=
filename_size
&&
!
memcmp
(
name
,
filename
,
filename_size
)
&&
(
name_size
<
filename_size
+
2
||
!
AV_RN16
(
name
+
filename_size
)))
return
wtvfile_open_sector
(
first_sector
,
file_length
,
depth
,
s
);
buf
+=
dir_length
;
}
return
0
;
}
#define wtvfile_open(s, buf, buf_size, filename) \
wtvfile_open2(s, buf, buf_size, filename, sizeof(filename))
/**
* Close file opened with wtvfile_open_sector(), or wtv_open()
*/
static
void
wtvfile_close
(
ByteIOContext
*
pb
)
{
WtvFile
*
wf
=
pb
->
opaque
;
av_free
(
wf
->
sectors
);
av_free
(
pb
);
}
/*
*
* Main demuxer
*
*/
typedef
struct
{
int
seen_data
;
}
WtvStream
;
typedef
struct
{
ByteIOContext
*
pb
;
/** timeline file */
int64_t
epoch
;
int64_t
pts
;
/** pts for next data chunk */
int64_t
last_valid_pts
;
/** latest valid pts, used for interative seeking */
/* maintain private seek index, as the AVIndexEntry->pos is relative to the
start of the 'timeline' file, not the file system (AVFormatContext->pb) */
AVIndexEntry
*
index_entries
;
int
nb_index_entries
;
unsigned
int
index_entries_allocated_size
;
}
WtvContext
;
typedef
struct
{
enum
CodecID
id
;
ff_asf_guid
guid
;
...
...
@@ -71,7 +344,7 @@ static enum CodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid g
/* WTV GUIDs */
static
const
ff_asf_guid
wtv_guid
=
{
0xB7
,
0xD8
,
0x00
,
0x20
,
0x37
,
0x49
,
0xDA
,
0x11
,
0xA6
,
0x4E
,
0x00
,
0x07
,
0xE9
,
0x5E
,
0xAD
,
0x8D
};
static
const
ff_asf_guid
meta_guid
=
static
const
ff_asf_guid
meta
data
_guid
=
{
0x5A
,
0xFE
,
0xD7
,
0x6D
,
0xC8
,
0x1D
,
0x8F
,
0x4A
,
0x99
,
0x22
,
0xFA
,
0xB1
,
0x1C
,
0x38
,
0x14
,
0x53
};
static
const
ff_asf_guid
timestamp_guid
=
{
0x5B
,
0x05
,
0xE6
,
0x1B
,
0x97
,
0xA9
,
0x49
,
0x43
,
0x88
,
0x17
,
0x1A
,
0x65
,
0x5A
,
0x29
,
0x8A
,
0x97
};
...
...
@@ -158,13 +431,152 @@ static int read_probe(AVProbeData *p)
return
ff_guidcmp
(
p
->
buf
,
wtv_guid
)
?
0
:
AVPROBE_SCORE_MAX
;
}
/**
* Convert win32 FILETIME to ISO-8601 string
*/
static
void
filetime_to_iso8601
(
char
*
buf
,
int
buf_size
,
int64_t
value
)
{
time_t
t
=
(
value
/
10000000LL
)
-
11644473600LL
;
strftime
(
buf
,
buf_size
,
"%Y-%m-%d %H:%M:%S"
,
gmtime
(
&
t
));
}
/**
* Convert crazy time (100ns since 1 Jan 0001) to ISO-8601 string
*/
static
void
crazytime_to_iso8601
(
char
*
buf
,
int
buf_size
,
int64_t
value
)
{
time_t
t
=
(
value
/
10000000LL
)
-
719162LL
*
86400LL
;
strftime
(
buf
,
buf_size
,
"%Y-%m-%d %H:%M:%S"
,
gmtime
(
&
t
));
}
/**
* Convert OLE DATE to ISO-8601 string
*/
static
void
oledate_to_iso8601
(
char
*
buf
,
int
buf_size
,
int64_t
value
)
{
time_t
t
=
631112400LL
+
86400
*
av_int2dbl
(
value
);
strftime
(
buf
,
buf_size
,
"%Y-%m-%d %H:%M:%S"
,
gmtime
(
&
t
));
}
static
void
get_attachment
(
AVFormatContext
*
s
,
ByteIOContext
*
pb
,
int
length
)
{
char
mime
[
1024
];
char
description
[
1024
];
unsigned
int
filesize
;
AVStream
*
st
;
int64_t
pos
=
url_ftell
(
pb
);
avio_get_str16le
(
pb
,
INT_MAX
,
mime
,
sizeof
(
mime
));
if
(
strcmp
(
mime
,
"image/jpeg"
))
goto
done
;
get_byte
(
pb
);
avio_get_str16le
(
pb
,
INT_MAX
,
description
,
sizeof
(
description
));
filesize
=
get_le32
(
pb
);
if
(
!
filesize
)
goto
done
;
st
=
av_new_stream
(
s
,
0
);
if
(
!
st
)
goto
done
;
av_metadata_set2
(
&
st
->
metadata
,
"title"
,
description
,
0
);
st
->
codec
->
codec_id
=
CODEC_ID_MJPEG
;
st
->
codec
->
codec_type
=
AVMEDIA_TYPE_ATTACHMENT
;
st
->
codec
->
extradata
=
av_mallocz
(
filesize
);
if
(
!
st
->
codec
->
extradata
)
goto
done
;
st
->
codec
->
extradata_size
=
filesize
;
get_buffer
(
pb
,
st
->
codec
->
extradata
,
filesize
);
done:
url_fseek
(
pb
,
pos
+
length
,
SEEK_SET
);
}
static
void
get_tag
(
AVFormatContext
*
s
,
ByteIOContext
*
pb
,
const
char
*
key
,
int
type
,
int
length
)
{
int
buf_size
=
FFMAX
(
2
*
length
,
LEN_PRETTY_GUID
)
+
1
;
char
*
buf
=
av_malloc
(
buf_size
);
if
(
!
buf
)
return
;
if
(
type
==
0
&&
length
==
4
)
{
snprintf
(
buf
,
buf_size
,
"%"
PRIi32
,
get_le32
(
pb
));
}
else
if
(
type
==
1
)
{
avio_get_str16le
(
pb
,
length
,
buf
,
buf_size
);
if
(
!
strlen
(
buf
))
{
av_free
(
buf
);
return
;
}
}
else
if
(
type
==
3
&&
length
==
4
)
{
strcpy
(
buf
,
get_le32
(
pb
)
?
"true"
:
"false"
);
}
else
if
(
type
==
4
&&
length
==
8
)
{
int64_t
num
=
get_le64
(
pb
);
if
(
!
strcmp
(
key
,
"WM/EncodingTime"
)
||
!
strcmp
(
key
,
"WM/MediaOriginalBroadcastDateTime"
))
filetime_to_iso8601
(
buf
,
buf_size
,
num
);
else
if
(
!
strcmp
(
key
,
"WM/WMRVEncodeTime"
)
||
!
strcmp
(
key
,
"WM/WMRVEndTime"
))
crazytime_to_iso8601
(
buf
,
buf_size
,
num
);
else
if
(
!
strcmp
(
key
,
"WM/WMRVExpirationDate"
))
oledate_to_iso8601
(
buf
,
buf_size
,
num
);
else
if
(
!
strcmp
(
key
,
"WM/WMRVBitrate"
))
snprintf
(
buf
,
buf_size
,
"%f"
,
av_int2dbl
(
num
));
else
snprintf
(
buf
,
buf_size
,
"%"
PRIi64
,
num
);
}
else
if
(
type
==
5
&&
length
==
2
)
{
snprintf
(
buf
,
buf_size
,
"%"
PRIi16
,
get_le16
(
pb
));
}
else
if
(
type
==
6
&&
length
==
16
)
{
ff_asf_guid
guid
;
get_buffer
(
pb
,
guid
,
16
);
snprintf
(
buf
,
buf_size
,
PRI_PRETTY_GUID
,
ARG_PRETTY_GUID
(
guid
));
}
else
if
(
type
==
2
&&
!
strcmp
(
key
,
"WM/Picture"
))
{
get_attachment
(
s
,
pb
,
length
);
av_freep
(
&
buf
);
return
;
}
else
{
av_freep
(
&
buf
);
av_log
(
s
,
AV_LOG_WARNING
,
"unsupported metadata entry; key:%s, type:%d, length:0x%x
\n
"
,
key
,
type
,
length
);
url_fskip
(
pb
,
length
);
return
;
}
av_metadata_set2
(
&
s
->
metadata
,
key
,
buf
,
0
);
av_freep
(
&
buf
);
}
/**
* Parse metadata entries
*/
static
void
parse_legacy_attrib
(
AVFormatContext
*
s
,
ByteIOContext
*
pb
)
{
ff_asf_guid
guid
;
int
length
,
type
;
while
(
!
url_feof
(
pb
))
{
char
key
[
1024
];
ff_get_guid
(
pb
,
&
guid
);
type
=
get_le32
(
pb
);
length
=
get_le32
(
pb
);
if
(
!
length
)
break
;
if
(
ff_guidcmp
(
&
guid
,
metadata_guid
))
{
av_log
(
s
,
AV_LOG_WARNING
,
"unknown guid "
PRI_GUID
", expected metadata_guid; "
"remaining metadata entries ignored
\n
"
,
ARG_GUID
(
guid
));
break
;
}
avio_get_str16le
(
pb
,
INT_MAX
,
key
,
sizeof
(
key
));
get_tag
(
s
,
pb
,
key
,
type
,
length
);
}
ff_metadata_conv
(
&
s
->
metadata
,
NULL
,
ff_asf_metadata_conv
);
}
/**
* parse VIDEOINFOHEADER2 structure
* @return bytes consumed
*/
static
int
parse_videoinfoheader2
(
AVFormatContext
*
s
,
AVStream
*
st
)
{
ByteIOContext
*
pb
=
s
->
pb
;
WtvContext
*
wtv
=
s
->
priv_data
;
ByteIOContext
*
pb
=
wtv
->
pb
;
url_fskip
(
pb
,
72
);
// picture aspect ratio is unreliable
ff_get_bmp_header
(
pb
,
st
);
...
...
@@ -233,7 +645,8 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
ff_asf_guid
mediatype
,
ff_asf_guid
subtype
,
ff_asf_guid
formattype
,
int
size
)
{
ByteIOContext
*
pb
=
s
->
pb
;
WtvContext
*
wtv
=
s
->
priv_data
;
ByteIOContext
*
pb
=
wtv
->
pb
;
if
(
!
ff_guidcmp
(
subtype
,
mediasubtype_cpfilters_processed
)
&&
!
ff_guidcmp
(
formattype
,
format_cpfilters_processed
))
{
ff_asf_guid
actual_subtype
;
...
...
@@ -339,32 +752,25 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
enum
{
SEEK_TO_DATA
=
0
,
SEEK_TO_BYTE
,
SEEK_TO_PTS
,
};
/**
* Parse WTV chunks
* @param mode SEEK_TO_DATA
, SEEK_TO_BYTE,
SEEK_TO_PTS
* @param seekts
either byte position or
timestamp
* @param mode SEEK_TO_DATA
or
SEEK_TO_PTS
* @param seekts timestamp
* @param[out] len Length of data chunk
* @return stream index of data chunk, or <0 on error
*/
static
int
parse_chunks
(
AVFormatContext
*
s
,
int
mode
,
int64_t
seekts
,
int
*
len_ptr
)
{
WtvContext
*
wtv
=
s
->
priv_data
;
ByteIOContext
*
pb
=
s
->
pb
;
ByteIOContext
*
pb
=
wtv
->
pb
;
while
(
!
url_feof
(
pb
))
{
ff_asf_guid
g
;
int
len
,
sid
,
consumed
;
if
(
mode
==
SEEK_TO_BYTE
&&
url_ftell
(
pb
)
>=
seekts
)
return
0
;
ff_get_guid
(
pb
,
&
g
);
if
(
is_zero
(
g
,
sizeof
(
ff_asf_guid
)))
return
AVERROR_EOF
;
len
=
get_le32
(
pb
);
if
(
len
<
32
)
break
;
...
...
@@ -458,11 +864,16 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
consumed
+=
16
;
if
(
wtv
->
pts
==
-
1
)
wtv
->
pts
=
AV_NOPTS_VALUE
;
else
{
wtv
->
last_valid_pts
=
wtv
->
pts
;
if
(
wtv
->
epoch
==
AV_NOPTS_VALUE
||
wtv
->
pts
<
wtv
->
epoch
)
wtv
->
epoch
=
wtv
->
pts
;
if
(
mode
==
SEEK_TO_PTS
&&
wtv
->
pts
>=
seekts
)
{
#define WTV_PAD8(x) (((x) + 7) & ~7)
url_fskip
(
pb
,
WTV_PAD8
(
len
)
-
consumed
);
return
0
;
}
}
}
}
else
if
(
!
ff_guidcmp
(
g
,
data_guid
))
{
int
stream_index
=
ff_find_stream_index
(
s
,
sid
);
...
...
@@ -472,8 +883,6 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
if
(
len_ptr
)
{
*
len_ptr
=
len
;
}
if
(
wtv
->
pts
!=
AV_NOPTS_VALUE
)
av_add_index_entry
(
s
->
streams
[
stream_index
],
url_ftell
(
pb
)
-
consumed
,
wtv
->
pts
,
0
,
0
,
AVINDEX_KEYFRAME
);
return
stream_index
;
}
}
else
if
(
...
...
@@ -505,26 +914,114 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
return
AVERROR_EOF
;
}
#define WTV_CHUNK_START 0x40000
/* declare utf16le strings */
#define _ , 0,
static
const
uint8_t
timeline_le16
[]
=
{
't'
_
'i'
_
'm'
_
'e'
_
'l'
_
'i'
_
'n'
_
'e'
,
0
};
static
const
uint8_t
table_0_entries_legacy_attrib_le16
[]
=
{
't'
_
'a'
_
'b'
_
'l'
_
'e'
_
'.'
_
'0'
_
'.'
_
'e'
_
'n'
_
't'
_
'r'
_
'i'
_
'e'
_
's'
_
'.'
_
'l'
_
'e'
_
'g'
_
'a'
_
'c'
_
'y'
_
'_'
_
'a'
_
't'
_
't'
_
'r'
_
'i'
_
'b'
,
0
};
static
const
uint8_t
table_0_entries_time_le16
[]
=
{
't'
_
'a'
_
'b'
_
'l'
_
'e'
_
'.'
_
'0'
_
'.'
_
'e'
_
'n'
_
't'
_
'r'
_
'i'
_
'e'
_
's'
_
'.'
_
't'
_
'i'
_
'm'
_
'e'
,
0
};
static
const
uint8_t
timeline_table_0_entries_Events_le16
[]
=
{
't'
_
'i'
_
'm'
_
'e'
_
'l'
_
'i'
_
'n'
_
'e'
_
'.'
_
't'
_
'a'
_
'b'
_
'l'
_
'e'
_
'.'
_
'0'
_
'.'
_
'e'
_
'n'
_
't'
_
'r'
_
'i'
_
'e'
_
's'
_
'.'
_
'E'
_
'v'
_
'e'
_
'n'
_
't'
_
's'
,
0
};
#undef _
static
int
read_header
(
AVFormatContext
*
s
,
AVFormatParameters
*
ap
)
{
ByteIOContext
*
pb
=
s
->
pb
;
WtvContext
*
wtv
=
s
->
priv_data
;
int
root_sector
,
root_size
;
uint8_t
root
[
WTV_SECTOR_SIZE
];
ByteIOContext
*
pb
;
int64_t
timeline_pos
;
int
ret
;
url_fseek
(
pb
,
WTV_CHUNK_START
,
SEEK_SET
);
wtv
->
epoch
=
wtv
->
pts
=
wtv
->
last_valid_pts
=
AV_NOPTS_VALUE
;
/* read root directory sector */
url_fskip
(
s
->
pb
,
0x30
);
root_size
=
get_le32
(
s
->
pb
);
if
(
root_size
>
sizeof
(
root
))
{
av_log
(
s
,
AV_LOG_ERROR
,
"root directory size exceeds sector size
\n
"
);
return
AVERROR_INVALIDDATA
;
}
url_fskip
(
s
->
pb
,
4
);
root_sector
=
get_le32
(
s
->
pb
);
url_fseek
(
s
->
pb
,
root_sector
<<
WTV_SECTOR_BITS
,
SEEK_SET
);
root_size
=
get_buffer
(
s
->
pb
,
root
,
root_size
);
if
(
root_size
<
0
)
return
AVERROR_INVALIDDATA
;
/* parse chunks up until first data chunk */
wtv
->
pb
=
wtvfile_open
(
s
,
root
,
root_size
,
timeline_le16
);
if
(
!
wtv
->
pb
)
{
av_log
(
s
,
AV_LOG_ERROR
,
"timeline data missing
\n
"
);
return
AVERROR_INVALIDDATA
;
}
ret
=
parse_chunks
(
s
,
SEEK_TO_DATA
,
0
,
0
);
if
(
ret
<
0
)
return
ret
;
url_fseek
(
wtv
->
pb
,
-
32
,
SEEK_CUR
);
timeline_pos
=
url_ftell
(
s
->
pb
);
// save before opening another file
/* read metadata */
pb
=
wtvfile_open
(
s
,
root
,
root_size
,
table_0_entries_legacy_attrib_le16
);
if
(
pb
)
{
parse_legacy_attrib
(
s
,
pb
);
wtvfile_close
(
pb
);
}
/* read seek index */
if
(
s
->
nb_streams
)
{
AVStream
*
st
=
s
->
streams
[
0
];
pb
=
wtvfile_open
(
s
,
root
,
root_size
,
table_0_entries_time_le16
);
if
(
pb
)
{
while
(
1
)
{
uint64_t
timestamp
=
get_le64
(
pb
);
uint64_t
frame_nb
=
get_le64
(
pb
);
if
(
url_feof
(
pb
))
break
;
ff_add_index_entry
(
&
wtv
->
index_entries
,
&
wtv
->
nb_index_entries
,
&
wtv
->
index_entries_allocated_size
,
0
,
timestamp
,
frame_nb
,
0
,
AVINDEX_KEYFRAME
);
}
wtvfile_close
(
pb
);
url_fseek
(
pb
,
-
32
,
SEEK_CUR
);
if
(
wtv
->
nb_index_entries
)
{
pb
=
wtvfile_open
(
s
,
root
,
root_size
,
timeline_table_0_entries_Events_le16
);
if
(
pb
)
{
int
i
;
while
(
1
)
{
uint64_t
frame_nb
=
get_le64
(
pb
);
uint64_t
position
=
get_le64
(
pb
);
if
(
url_feof
(
pb
))
break
;
for
(
i
=
wtv
->
nb_index_entries
-
1
;
i
>=
0
;
i
--
)
{
AVIndexEntry
*
e
=
wtv
->
index_entries
+
i
;
if
(
frame_nb
>
e
->
size
)
break
;
if
(
position
>
e
->
pos
)
e
->
pos
=
position
;
}
}
wtvfile_close
(
pb
);
st
->
duration
=
wtv
->
index_entries
[
wtv
->
nb_index_entries
-
1
].
timestamp
;
}
}
}
}
url_fseek
(
s
->
pb
,
timeline_pos
,
SEEK_SET
);
return
0
;
}
static
int
read_packet
(
AVFormatContext
*
s
,
AVPacket
*
pkt
)
{
WtvContext
*
wtv
=
s
->
priv_data
;
ByteIOContext
*
pb
=
s
->
pb
;
ByteIOContext
*
pb
=
wtv
->
pb
;
int
stream_index
,
len
,
ret
;
stream_index
=
parse_chunks
(
s
,
SEEK_TO_DATA
,
0
,
&
len
);
...
...
@@ -540,59 +1037,47 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
return
0
;
}
static
int
read_seek
2
(
AVFormatContext
*
s
,
int
stream_index
,
int64_t
min_ts
,
int64_t
ts
,
int64_t
max_
ts
,
int
flags
)
static
int
read_seek
(
AVFormatContext
*
s
,
int
stream_index
,
int64_t
ts
,
int
flags
)
{
WtvContext
*
wtv
=
s
->
priv_data
;
ByteIOContext
*
pb
=
s
->
pb
;
AVStream
*
st
;
ByteIOContext
*
pb
=
wtv
->
pb
;
AVStream
*
st
=
s
->
streams
[
0
];
int64_t
ts_relative
;
int
i
;
if
(
stream_index
<
0
)
{
stream_index
=
av_find_default_stream_index
(
s
);
if
(
stream_index
<
0
)
return
-
1
;
}
st
=
s
->
streams
[
stream_index
];
if
((
flags
&
AVSEEK_FLAG_FRAME
))
{
if
((
flags
&
AVSEEK_FLAG_FRAME
)
||
(
flags
&
AVSEEK_FLAG_BYTE
))
return
AVERROR_NOTSUPP
;
}
else
if
((
flags
&
AVSEEK_FLAG_BYTE
))
{
if
(
ts
<
url_ftell
(
pb
))
{
for
(
i
=
st
->
nb_index_entries
-
1
;
i
>=
0
;
i
--
)
{
if
(
st
->
index_entries
[
i
].
pos
<=
ts
)
{
wtv
->
pts
=
st
->
index_entries
[
i
].
timestamp
;
url_fseek
(
pb
,
st
->
index_entries
[
i
].
pos
,
SEEK_SET
);
break
;
}
}
if
(
i
<
0
)
{
wtv
->
pts
=
0
;
url_fseek
(
pb
,
WTV_CHUNK_START
,
SEEK_SET
);
}
}
if
(
parse_chunks
(
s
,
SEEK_TO_BYTE
,
ts
,
0
)
<
0
)
/* timestamp adjustment is required because wtv->pts values are absolute,
* whereas AVIndexEntry->timestamp values are relative to epoch. */
ts_relative
=
ts
;
if
(
wtv
->
epoch
!=
AV_NOPTS_VALUE
)
ts_relative
-=
wtv
->
epoch
;
i
=
ff_index_search_timestamp
(
wtv
->
index_entries
,
wtv
->
nb_index_entries
,
ts_relative
,
flags
);
if
(
i
<
0
)
{
if
(
wtv
->
last_valid_pts
==
AV_NOPTS_VALUE
||
ts
<
wtv
->
last_valid_pts
)
url_fseek
(
pb
,
0
,
SEEK_SET
);
else
if
(
st
->
duration
!=
AV_NOPTS_VALUE
&&
ts_relative
>
st
->
duration
&&
wtv
->
nb_index_entries
)
url_fseek
(
pb
,
wtv
->
index_entries
[
wtv
->
nb_index_entries
-
1
].
pos
,
SEEK_SET
);
if
(
parse_chunks
(
s
,
SEEK_TO_PTS
,
ts
,
0
)
<
0
)
return
AVERROR
(
ERANGE
);
return
0
;
}
else
{
ts
*=
10
;
i
=
av_index_search_timestamp
(
st
,
ts
,
flags
);
if
(
i
<
0
)
{
if
(
st
->
nb_index_entries
>
0
)
{
wtv
->
pts
=
st
->
index_entries
[
st
->
nb_index_entries
-
1
].
timestamp
;
url_fseek
(
pb
,
st
->
index_entries
[
st
->
nb_index_entries
-
1
].
pos
,
SEEK_SET
);
}
else
{
wtv
->
pts
=
0
;
url_fseek
(
pb
,
WTV_CHUNK_START
,
SEEK_SET
);
}
if
(
parse_chunks
(
s
,
SEEK_TO_PTS
,
ts
,
0
)
<
0
)
return
AVERROR
(
ERANGE
);
return
0
;
}
wtv
->
pts
=
st
->
index_entries
[
i
].
timestamp
;
url_fseek
(
pb
,
st
->
index_entries
[
i
].
pos
,
SEEK_SET
);
return
0
;
}
wtv
->
pts
=
wtv
->
index_entries
[
i
].
timestamp
;
if
(
wtv
->
epoch
!=
AV_NOPTS_VALUE
)
wtv
->
pts
+=
wtv
->
epoch
;
wtv
->
last_valid_pts
=
wtv
->
pts
;
url_fseek
(
pb
,
wtv
->
index_entries
[
i
].
pos
,
SEEK_SET
);
return
0
;
}
static
int
read_close
(
AVFormatContext
*
s
)
{
WtvContext
*
wtv
=
s
->
priv_data
;
wtvfile_close
(
wtv
->
pb
);
return
0
;
}
AVInputFormat
ff_wtv_demuxer
=
{
...
...
@@ -602,6 +1087,7 @@ AVInputFormat ff_wtv_demuxer = {
.
read_probe
=
read_probe
,
.
read_header
=
read_header
,
.
read_packet
=
read_packet
,
.
read_seek2
=
read_seek2
,
.
flags
=
AVFMT_SHOW_IDS
|
AVFMT_TS_DISCONT
,
.
read_seek
=
read_seek
,
.
read_close
=
read_close
,
.
flags
=
AVFMT_SHOW_IDS
,
};
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录