Compare commits
4 Commits
v1.8.0-alp
...
v1.8.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5229141d02 | ||
|
|
18cd747be9 | ||
|
|
7e4f0620a2 | ||
|
|
a5cd17c050 |
2
.github/workflows/docker-hub.yml
vendored
2
.github/workflows/docker-hub.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
|
||||
2
.github/workflows/ghcr.yml
vendored
2
.github/workflows/ghcr.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
|
||||
2
.github/workflows/portable.yml
vendored
2
.github/workflows/portable.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v3
|
||||
|
||||
2
.github/workflows/pypi.yml
vendored
2
.github/workflows/pypi.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v3
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 更新日志
|
||||
|
||||
## 1.8.0-alpha.1
|
||||
|
||||
- 重构
|
||||
- 修 bug
|
||||
|
||||
## 1.8.0-alpha
|
||||
|
||||
- 重构直播流录制
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
__prog__ = 'blrec'
|
||||
__version__ = '1.8.0-alpha'
|
||||
__version__ = '1.8.0-alpha.1'
|
||||
__github__ = 'https://github.com/acgnhiki/blrec'
|
||||
|
||||
@@ -32,7 +32,7 @@ class MetadataDumper(SwitchableMixin):
|
||||
|
||||
def _do_enable(self) -> None:
|
||||
self._metadata_subscription = self._analyser.metadatas.subscribe(
|
||||
self._update_metadata
|
||||
on_next=self._update_metadata, on_error=self._reset_metadata
|
||||
)
|
||||
self._join_points_subscription = (
|
||||
self._joinpoint_extractor.join_points.subscribe(self._update_join_points)
|
||||
@@ -54,6 +54,9 @@ class MetadataDumper(SwitchableMixin):
|
||||
def _update_metadata(self, metadata: flv_ops.MetaData) -> None:
|
||||
self._last_metadata = metadata
|
||||
|
||||
def _reset_metadata(self, exc: Exception) -> None:
|
||||
self._last_metadata = None
|
||||
|
||||
def _update_join_points(self, join_points: List[flv_ops.JoinPoint]) -> None:
|
||||
self._last_join_points = join_points
|
||||
|
||||
|
||||
@@ -178,9 +178,13 @@ class Analyser:
|
||||
)
|
||||
|
||||
def make_metadata(self) -> MetaData:
|
||||
assert self._has_audio == self._audio_analysed
|
||||
assert self._has_video and self._video_analysed
|
||||
assert self._resolution is not None
|
||||
assert self._has_audio == self._audio_analysed, (
|
||||
f'has_audio: {self._has_audio}, audio_analysed: {self._audio_analysed}',
|
||||
)
|
||||
assert self._has_video and self._video_analysed, (
|
||||
f'has_video: {self._has_video}, video_analysed: {self._video_analysed}',
|
||||
)
|
||||
assert self._resolution is not None, 'no resolution'
|
||||
|
||||
if not self._has_audio:
|
||||
audiosize = None
|
||||
@@ -241,7 +245,7 @@ class Analyser:
|
||||
metadata = self.make_metadata()
|
||||
except Exception as e:
|
||||
logger.warning(f'Failed to make metadata: {repr(e)}')
|
||||
pass
|
||||
self._metadatas.on_error(e)
|
||||
else:
|
||||
self._metadatas.on_next(metadata)
|
||||
|
||||
@@ -302,6 +306,7 @@ class Analyser:
|
||||
self._audio_sample_rate = tag.sound_rate.value
|
||||
self._audio_sample_size = tag.sound_size.value
|
||||
self._stereo = tag.sound_type == SoundType.STEREO
|
||||
logger.debug(f'Audio analysed: {tag}')
|
||||
|
||||
self._num_of_audio_tags += 1
|
||||
self._size_of_audio_tags += tag.tag_size
|
||||
@@ -314,6 +319,7 @@ class Analyser:
|
||||
self._keyframe_filepositions.append(self.calc_file_size())
|
||||
if tag.is_avc_header():
|
||||
self._resolution = Resolution.from_aac_sequence_header(tag)
|
||||
logger.debug(f'Resolution: {self._resolution}')
|
||||
else:
|
||||
pass
|
||||
|
||||
@@ -321,6 +327,7 @@ class Analyser:
|
||||
self._has_video = True
|
||||
self._video_analysed = True
|
||||
self._video_codec_id = tag.codec_id.value
|
||||
logger.debug(f'Video analysed: {tag}')
|
||||
|
||||
self._num_of_video_tags += 1
|
||||
self._size_of_video_tags += tag.tag_size
|
||||
|
||||
@@ -32,8 +32,8 @@ logger = logging.getLogger(__name__)
|
||||
@attr.s(auto_attribs=True, slots=True, frozen=True)
|
||||
class JoinPoint:
|
||||
seamless: bool
|
||||
timestamp: float # timestamp of previous tag in milliseconds
|
||||
crc32: str # crc32 of the previous tag
|
||||
timestamp: float # timestamp of next tag in milliseconds
|
||||
crc32: str # crc32 of the next tag
|
||||
|
||||
@classmethod
|
||||
def from_metadata_value(cls, value: JoinPointData) -> JoinPoint:
|
||||
@@ -181,13 +181,14 @@ def concat(
|
||||
return tag
|
||||
return tag.evolve(timestamp=tag.timestamp + delta)
|
||||
|
||||
def make_join_point_tag(tag: FlvTag, seamless: bool) -> ScriptTag:
|
||||
assert tag.body is not None
|
||||
def make_join_point_tag(next_tag: FlvTag, seamless: bool) -> ScriptTag:
|
||||
assert next_tag.body is not None
|
||||
join_point = JoinPoint(
|
||||
seamless=seamless,
|
||||
timestamp=float(tag.timestamp),
|
||||
crc32=cksum(tag.body),
|
||||
timestamp=float(next_tag.timestamp),
|
||||
crc32=cksum(next_tag.body),
|
||||
)
|
||||
logger.debug(f'join point: {join_point}; next tag: {next_tag}')
|
||||
script_data = ScriptData(
|
||||
name='onJoinPoint', value=attr.asdict(join_point)
|
||||
)
|
||||
@@ -227,13 +228,15 @@ def concat(
|
||||
update_delta_no_duplicated(tags[0])
|
||||
logger.debug(f'Updated delta: {delta}, seamless: {seamless}')
|
||||
|
||||
join_point_tag = make_join_point_tag(last_tags[-1], seamless)
|
||||
observer.on_next(join_point_tag)
|
||||
if tags:
|
||||
join_point_tag = make_join_point_tag(correct_ts(tags[0]), seamless)
|
||||
observer.on_next(join_point_tag)
|
||||
|
||||
for tag in tags:
|
||||
tag = correct_ts(tag)
|
||||
update_last_tags(tag)
|
||||
observer.on_next(tag)
|
||||
|
||||
gathered_tags.clear()
|
||||
|
||||
def do_cancel() -> None:
|
||||
@@ -361,7 +364,6 @@ class JoinPointExtractor:
|
||||
if join_point_tag:
|
||||
join_point = self._make_join_point(join_point_tag, item)
|
||||
join_points.append(join_point)
|
||||
logger.debug(f'{repr(join_point)}; {join_point}')
|
||||
join_point_tag = None
|
||||
|
||||
if self._is_join_point_tag(item):
|
||||
@@ -395,12 +397,20 @@ class JoinPointExtractor:
|
||||
return script_data['name'] == 'onJoinPoint'
|
||||
return False
|
||||
|
||||
def _make_join_point(self, join_point_tag: ScriptTag, tag: FlvTag) -> JoinPoint:
|
||||
assert tag.body is not None
|
||||
def _make_join_point(
|
||||
self, join_point_tag: ScriptTag, next_tag: FlvTag
|
||||
) -> JoinPoint:
|
||||
script_data = parse_scriptdata(join_point_tag)
|
||||
join_point_data = cast(JoinPointData, script_data['value'])
|
||||
return JoinPoint(
|
||||
seamless=join_point_data['seamless'],
|
||||
timestamp=tag.timestamp,
|
||||
crc32=cksum(tag.body),
|
||||
assert next_tag.body is not None, next_tag
|
||||
assert cksum(next_tag.body) == join_point_data['crc32'], (
|
||||
join_point_tag,
|
||||
next_tag,
|
||||
)
|
||||
join_point = JoinPoint(
|
||||
seamless=join_point_data['seamless'],
|
||||
timestamp=next_tag.timestamp,
|
||||
crc32=join_point_data['crc32'],
|
||||
)
|
||||
logger.debug(f'Extracted join point: {join_point}; next tag: {next_tag}')
|
||||
return join_point
|
||||
|
||||
@@ -52,9 +52,9 @@ class Injector:
|
||||
logger.debug('Injected metadata into the metadata tag')
|
||||
else:
|
||||
logger.debug('No metadata tag in the stream')
|
||||
tag = self._make_metadata_tag()
|
||||
metadata_tag = self._make_metadata_tag()
|
||||
logger.debug('Maked a metadata tag for metadata injection')
|
||||
observer.on_next(tag)
|
||||
observer.on_next(metadata_tag)
|
||||
logger.debug('Inserted the artificial metadata tag')
|
||||
|
||||
observer.on_next(tag)
|
||||
|
||||
@@ -133,8 +133,10 @@ class Postprocessor(
|
||||
self._postprocessing_progress = None
|
||||
|
||||
video_path = await self._queue.get()
|
||||
logger.debug(f'Postprocessing... {video_path}')
|
||||
|
||||
if not await self._is_vaild_flv_file(video_path):
|
||||
logger.warning(f'Invalid flv file: {video_path}')
|
||||
self._queue.task_done()
|
||||
continue
|
||||
|
||||
|
||||
Reference in New Issue
Block a user