1515from abc import ABC , abstractmethod
1616from bisect import bisect
1717from collections .abc import Generator , Iterable , Iterator , Sequence
18- from contextlib import AbstractContextManager , ExitStack , closing , contextmanager
18+ from contextlib import ExitStack , closing
1919from dataclasses import dataclass
2020from enum import IntEnum
21+ from fractions import Fraction
2122from random import shuffle
2223from typing import Any , Callable , Optional , Protocol , TypeVar , Union
2324
@@ -571,28 +572,14 @@ def extract(self):
571572 os .remove (self ._zip_source .filename )
572573
573574class _AvVideoReading :
574- @contextmanager
575575 def read_av_container (
576576 self , source : Union [str , io .BytesIO ]
577- ) -> Generator [ av .container .InputContainer , None , None ] :
577+ ) -> av .container .InputContainer :
578578 if isinstance (source , io .BytesIO ):
579579 source .seek (0 ) # required for re-reading
580580
581- container = av .open (source )
582- try :
583- yield container
584- finally :
585- # fixes a memory leak in input container closing
586- # https://github.com/PyAV-Org/PyAV/issues/1117
587- for stream in container .streams :
588- context = stream .codec_context
589- if context and context .is_open :
590- # Currently, context closing may get stuck on some videos for an unknown reason,
591- # so the thread_type == 'AUTO' setting is disabled for future investigation
592- context .close ()
593-
594- if container .open_files :
595- container .close ()
581+ return av .open (source )
582+
596583
597584 def decode_stream (
598585 self , container : av .container .Container , video_stream : av .video .stream .VideoStream
@@ -679,13 +666,10 @@ def iterate_frames(
679666 with closing (self ._decode_stream (container , video_stream )) as stream_decoder :
680667 for frame , frame_number in zip (stream_decoder , frame_counter ):
681668 if frame_number == next_frame_filter_frame :
682- if video_stream . metadata . get ( 'rotate' ) :
669+ if frame . rotation :
683670 pts = frame .pts
684671 frame = av .VideoFrame ().from_ndarray (
685- rotate_image (
686- frame .to_ndarray (format = 'bgr24' ),
687- 360 - int (video_stream .metadata .get ('rotate' ))
688- ),
672+ rotate_image (frame .to_ndarray (format = 'bgr24' ), frame .rotation ),
689673 format = 'bgr24'
690674 )
691675 frame .pts = pts
@@ -707,7 +691,7 @@ def get_progress(self, pos):
707691 duration = self ._get_duration ()
708692 return pos / duration if duration else None
709693
710- def _read_av_container (self ) -> AbstractContextManager [ av .container .InputContainer ] :
694+ def _read_av_container (self ) -> av .container .InputContainer :
711695 return _AvVideoReading ().read_av_container (self ._source_path [0 ])
712696
713697 def _decode_stream (
@@ -798,7 +782,7 @@ def __init__(self, manifest_path: str, source_path: str, *, allow_threading: boo
798782
799783 self .allow_threading = allow_threading
800784
801- def _read_av_container (self ) -> AbstractContextManager [ av .container .InputContainer ] :
785+ def _read_av_container (self ) -> av .container .InputContainer :
802786 return _AvVideoReading ().read_av_container (self .source_path )
803787
804788 def _decode_stream (
@@ -843,12 +827,9 @@ def iterate_frames(self, *, frame_filter: Iterable[int]) -> Iterable[av.VideoFra
843827 with closing (self ._decode_stream (container , video_stream )) as stream_decoder :
844828 for frame , frame_number in zip (stream_decoder , frame_counter ):
845829 if frame_number == next_frame_filter_frame :
846- if video_stream . metadata . get ( 'rotate' ) :
830+ if frame . rotation :
847831 frame = av .VideoFrame ().from_ndarray (
848- rotate_image (
849- frame .to_ndarray (format = 'bgr24' ),
850- 360 - int (video_stream .metadata .get ('rotate' ))
851- ),
832+ rotate_image (frame .to_ndarray (format = 'bgr24' ), frame .rotation ),
852833 format = 'bgr24'
853834 )
854835
@@ -1050,7 +1031,7 @@ def __init__(self, quality=67):
10501031 "preset" : "ultrafast" ,
10511032 }
10521033
1053- def _add_video_stream (self , container : av .container .OutputContainer , w , h , rate , options ):
1034+ def _add_video_stream (self , container : av .container .OutputContainer , w , h , rate , options ) -> av . video . stream . VideoStream :
10541035 # x264 requires width and height must be divisible by 2 for yuv420p
10551036 if h % 2 :
10561037 h += 1
@@ -1067,6 +1048,14 @@ def _add_video_stream(self, container: av.container.OutputContainer, w, h, rate,
10671048 video_stream .pix_fmt = "yuv420p"
10681049 video_stream .width = w
10691050 video_stream .height = h
1051+
1052+ if "profile" in options :
1053+ video_stream .profile = options ["profile" ]
1054+ if "qmin" in options :
1055+ video_stream .codec_context .qmin = int (options ["qmin" ])
1056+ video_stream .codec_context .qmax = int (options ["qmax" ])
1057+ options = {k : options [k ] for k in options if k not in ("profile" , "qmin" , "qmax" )}
1058+
10701059 video_stream .options = options
10711060
10721061 return video_stream
@@ -1103,8 +1092,7 @@ def save_as_chunk(
11031092 options = self ._codec_opts ,
11041093 )
11051094
1106- with closing (output_v_stream ):
1107- self ._encode_images (images , output_container , output_v_stream )
1095+ self ._encode_images (images , output_container , output_v_stream )
11081096
11091097 return [(input_w , input_h )]
11101098
@@ -1115,7 +1103,7 @@ def _encode_images(
11151103 for frame , _ , _ in images :
11161104 # let libav set the correct pts and time_base
11171105 frame .pts = None
1118- frame .time_base = None
1106+ frame .time_base = Fraction ( 0 , 1 )
11191107
11201108 for packet in stream .encode (frame ):
11211109 container .mux (packet )
@@ -1160,8 +1148,7 @@ def save_as_chunk(self, images, chunk_path):
11601148 options = self ._codec_opts ,
11611149 )
11621150
1163- with closing (output_v_stream ):
1164- self ._encode_images (images , output_container , output_v_stream )
1151+ self ._encode_images (images , output_container , output_v_stream )
11651152
11661153 return [(input_w , input_h )]
11671154
0 commit comments