forked from PyAV-Org/PyAV
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodeccontext.pyx
More file actions
153 lines (120 loc) · 5.29 KB
/
Copy pathcodeccontext.pyx
File metadata and controls
153 lines (120 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
cimport libav as lib
from av.audio.format cimport AudioFormat, get_audio_format
from av.audio.layout cimport AudioLayout, get_audio_layout
from av.audio.frame cimport AudioFrame, alloc_audio_frame
from av.frame cimport Frame
from av.packet cimport Packet
from av.utils cimport err_check
cdef class AudioCodecContext(CodecContext):
cdef _init(self, lib.AVCodecContext *ptr, lib.AVCodec *codec):
CodecContext._init(self, ptr, codec)
# Sometimes there isn't a layout set, but there are a number of
# channels. Assume it is the default layout.
# TODO: Put this behind `not bare_metal`.
# TODO: Do this more efficiently.
if self.ptr.channels and not self.ptr.channel_layout:
self.ptr.channel_layout = get_audio_layout(self.ptr.channels, 0).layout
cdef _encode(self, Frame input_frame):
"""Encodes a frame of audio, returns a packet if one is ready.
The output packet does not necessarily contain data for the most recent frame,
as encoders can delay, split, and combine input frames internally as needed.
If called with with no args it will flush out the encoder and return the buffered
packets until there are none left, at which it will return None.
"""
cdef bint is_flushing = input_frame is None
cdef AudioFrame frame = input_frame
# Resample. A None frame will flush the resampler, and then the fifo (if used).
if not self.resampler:
self.resampler = AudioResampler(
self.format,
self.layout,
self.ptr.sample_rate
)
frame = self.resampler.resample(frame)
cdef bint use_fifo = not (self.ptr.codec.capabilities & lib.CODEC_CAP_VARIABLE_FRAME_SIZE)
if use_fifo:
print 'USING FIFO; frame_size:', self.ptr.frame_size
if not self.fifo:
self.fifo = AudioFifo()
if frame:
self.fifo.write(frame)
# Pull partial frames if we were requested to flush (via a None frame).
frame = self.fifo.read(self.ptr.frame_size, partial=is_flushing)
cdef Packet packet = Packet()
cdef int got_packet = 0
if frame is not None:
# TODO: Centralize time handling.
# If the frame has a valid pts, scale it to the codec's time_base.
# Remember that the AudioFifo time_base is always 1/sample_rate!
if frame.ptr.pts != lib.AV_NOPTS_VALUE:
frame.ptr.pts = lib.av_rescale_q(
frame.ptr.pts,
frame._time_base,
self.ptr.time_base
)
else:
frame.ptr.pts = lib.av_rescale(
self.ptr.frame_number,
self.ptr.sample_rate,
self.ptr.frame_size,
)
# TODO codec-ctx: streams rebased pts/dts/duration from self.ptr.time_base to self._stream.time_base
err_check(lib.avcodec_encode_audio2(
self.ptr,
&packet.struct,
frame.ptr if frame is not None else NULL,
&got_packet,
))
if got_packet:
return packet
cdef _decode_one(self, lib.AVPacket *packet, int *data_consumed):
if not self.next_frame:
self.next_frame = alloc_audio_frame()
cdef int completed_frame = 0
data_consumed[0] = err_check(lib.avcodec_decode_audio4(self.ptr, self.next_frame.ptr, &completed_frame, packet))
if not completed_frame:
return
cdef AudioFrame frame = self.next_frame
self.next_frame = None
frame._init_properties()
return frame
property frame_size:
"""Number of samples per channel in an audio frame."""
def __get__(self): return self.ptr.frame_size
property sample_rate:
"""Number samples of per second."""
def __get__(self):
return self.ptr.sample_rate
def __set__(self, int value):
self.ptr.sample_rate = value
# TODO: Deprecate.
property rate:
"""Number samples of per second."""
def __get__(self):
return self.ptr.sample_rate
def __set__(self, int value):
self.ptr.sample_rate = value
# TODO: Integrate into AudioLayout.
property channels:
def __get__(self):
return self.ptr.channels
def __set__(self, value):
self.ptr.channels = value
self.ptr.channel_layout = lib.av_get_default_channel_layout(value)
property channel_layout:
def __get__(self):
return self.ptr.channel_layout
property layout:
def __get__(self):
return get_audio_layout(self.ptr.channels, self.ptr.channel_layout)
def __set__(self, value):
cdef AudioLayout layout = AudioLayout(value)
print 'SETTING LAYOUT TO', layout
self.ptr.channel_layout = layout.layout
self.ptr.channels = layout.nb_channels
property format:
def __get__(self):
return get_audio_format(self.ptr.sample_fmt)
def __set__(self, value):
cdef AudioFormat format = AudioFormat(value)
self.ptr.sample_fmt = format.sample_fmt