Skip to content

Commit ecad0bb

Browse files
committed
Allow all known hardware encoders
1 parent 3ef0653 commit ecad0bb

File tree

7 files changed

+44
-19
lines changed

7 files changed

+44
-19
lines changed

CHANGELOG.md

+15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1+
# 26.2.0 (Unreleased)
2+
3+
## Features
4+
- Allow all hardware encoders PyAV knows about (h264_videotoolbox, hevc_nvenc, etc.).
5+
- New option `-vprofile`. Allows setting the video profile.
6+
7+
## Misc.
8+
- Deprecate the `copy` codec (auto-editor always re-encoders no matter what).
9+
10+
**Full Changelog**: https://github.com/WyattBlue/auto-editor/compare/26.1.2...26.2.0
11+
12+
113
# 26.1.2
214

315
## Fixes
416
- Use file name, not full path for cache
517

618

19+
**Full Changelog**: https://github.com/WyattBlue/auto-editor/compare/26.1.1...26.1.2
20+
21+
722
# 26.1.1
823

924
## Fixes

auto_editor/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "26.1.2"
1+
__version__ = "26.2.0"

auto_editor/edit.py

+18-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any
99

1010
import av
11-
from av import AudioResampler
11+
from av import AudioResampler, Codec
1212

1313
from auto_editor.ffwrapper import FileInfo, initFileInfo
1414
from auto_editor.lib.contracts import is_int, is_str
@@ -81,14 +81,21 @@ def set_video_codec(
8181
return codec
8282

8383
if codec == "copy":
84+
log.deprecated("The `copy` codec is deprecated. auto-editor always re-encodes")
8485
if src is None:
8586
log.error("No input to copy its codec from.")
8687
if not src.videos:
8788
log.error("Input file does not have a video stream to copy codec from.")
8889
codec = src.videos[0].codec
8990

9091
if ctr.vcodecs is not None and codec not in ctr.vcodecs:
91-
log.error(codec_error.format(codec, out_ext))
92+
try:
93+
cobj = Codec(codec, "w")
94+
except av.codec.codec.UnknownCodecError:
95+
log.error(f"Unknown encoder: {codec}")
96+
# Normalize encoder names
97+
if cobj.id not in (Codec(x, "w").id for x in ctr.vcodecs):
98+
log.error(codec_error.format(codec, out_ext))
9299

93100
return codec
94101

@@ -101,26 +108,30 @@ def set_audio_codec(
101108
codec = "aac"
102109
else:
103110
codec = src.audios[0].codec
104-
ctx = av.Codec(codec)
105-
if ctx.audio_formats is None:
111+
if av.Codec(codec, "w").audio_formats is None:
106112
codec = "aac"
107113
if codec not in ctr.acodecs and ctr.default_aud != "none":
108114
codec = ctr.default_aud
109-
if codec == "mp3float":
110-
codec = "mp3"
111115
if codec is None:
112116
codec = "aac"
113117
return codec
114118

115119
if codec == "copy":
120+
log.deprecated("The `copy` codec is deprecated. auto-editor always re-encodes")
116121
if src is None:
117122
log.error("No input to copy its codec from.")
118123
if not src.audios:
119124
log.error("Input file does not have an audio stream to copy codec from.")
120125
codec = src.audios[0].codec
121126

122127
if ctr.acodecs is None or codec not in ctr.acodecs:
123-
log.error(codec_error.format(codec, out_ext))
128+
try:
129+
cobj = Codec(codec, "w")
130+
except av.codec.codec.UnknownCodecError:
131+
log.error(f"Unknown encoder: {codec}")
132+
# Normalize encoder names
133+
if cobj.id not in (Codec(x, "w").id for x in ctr.acodecs):
134+
log.error(codec_error.format(codec, out_ext))
124135

125136
return codec
126137

auto_editor/ffwrapper.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ def initFileInfo(path: str, log: Log) -> FileInfo:
147147
adur = float(a.duration * a.time_base)
148148

149149
a_cc = a.codec_context
150+
name = a_cc.name if a_cc.name != "mp3float" else "mp3"
150151
audios += (
151152
AudioStream(
152-
a_cc.name,
153+
name,
153154
0 if a_cc.sample_rate is None else a_cc.sample_rate,
154155
a.layout.name,
155156
a_cc.channels,

auto_editor/render/video.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,15 @@ def render_av(
148148
ops = {"mov_flags": "faststart"}
149149
output_stream = output.add_stream(args.video_codec, rate=target_fps, options=ops)
150150

151+
cc = output_stream.codec_context
151152
if args.vprofile is not None:
152-
if args.vprofile.title() not in output_stream.codec_context.profiles:
153-
a = [f'"{x.lower()}"' for x in output_stream.codec_context.profiles]
154-
b = " ".join(a)
153+
if args.vprofile.title() not in cc.profiles:
154+
b = " ".join([f'"{x.lower()}"' for x in cc.profiles])
155155
log.error(
156156
f"`{args.vprofile}` is not a valid profile.\nprofiles supported: {b}"
157157
)
158158

159-
output_stream.codec_context.profile = args.vprofile.title()
159+
cc.profile = args.vprofile.title()
160160

161161
yield output_stream
162162
if not isinstance(output_stream, av.VideoStream):

auto_editor/utils/container.py

-6
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,6 @@ def container_constructor(ext: str) -> Container:
8282
kind = codec_type(codec)
8383
if kind == "video":
8484
vcodecs.add(codec)
85-
if codec == "h264":
86-
vcodecs.add("libx264")
87-
if codec == "av1":
88-
vcodecs.add("libsvtav1")
89-
if codec == "hevc":
90-
vcodecs.add("hevc_nvenc")
9185
if kind == "audio":
9286
acodecs.add(codec)
9387
if kind == "subtitle":

auto_editor/utils/log.py

+4
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def experimental(self, codec: av.Codec) -> None:
104104
if codec.experimental:
105105
self.error(f"`{codec.name}` is an experimental codec")
106106

107+
@staticmethod
108+
def deprecated(message: str) -> None:
109+
sys.stderr.write(f"\033[1m\033[33m{message}\033[0m\n")
110+
107111
def error(self, message: str | Exception) -> NoReturn:
108112
if self.is_debug and isinstance(message, Exception):
109113
self.cleanup()

0 commit comments

Comments
 (0)