yta-video-opengl 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ that, using ffmpeg, detects the video.
4
4
  """
5
5
  from yta_video_opengl.reader.cache import VideoFrameCache
6
6
  from yta_video_opengl.utils import iterate_stream_frames_demuxing
7
+ from yta_video_opengl.t import T
7
8
  from yta_validation import PythonValidator
8
9
  from av.video.frame import VideoFrame
9
10
  from av.audio.frame import AudioFrame
@@ -11,7 +12,7 @@ from av.packet import Packet
11
12
  from av.video.stream import VideoStream
12
13
  from av.audio.stream import AudioStream
13
14
  from av.container.input import InputContainer
14
- from fractions import Fraction
15
+ from quicktions import Fraction
15
16
  from av import open as av_open
16
17
  from typing import Union
17
18
  from dataclasses import dataclass
@@ -58,7 +59,7 @@ class VideoReaderFrame:
58
59
  self,
59
60
  # TODO: Add the type, please
60
61
  frame: any,
61
- t: float = None,
62
+ t: Union[int, float, Fraction] = None,
62
63
  pixel_format: str = 'rgb24'
63
64
  ):
64
65
  self.value: Union[AudioFrame, VideoFrame] = frame
@@ -66,7 +67,7 @@ class VideoReaderFrame:
66
67
  The frame content, that can be audio or video
67
68
  frame.
68
69
  """
69
- self.t: float = t
70
+ self.t: Fraction = Fraction(t)
70
71
  """
71
72
  The 't' time moment of the frame.
72
73
  """
@@ -139,6 +140,8 @@ class VideoReader:
139
140
  """
140
141
  return self.container.decode(self.video_stream)
141
142
 
143
+ # TODO: Remove this when possible, we are not
144
+ # using 'next' properties
142
145
  @property
143
146
  def next_frame(
144
147
  self
@@ -159,16 +162,6 @@ class VideoReader:
159
162
  """
160
163
  return self.container.decode(self.audio_stream)
161
164
 
162
- @property
163
- def next_audio_frame(
164
- self
165
- ) -> Union[AudioFrame, None]:
166
- """
167
- Get the next audio frame (decoded) from the
168
- iterator.
169
- """
170
- return next(self.audio_frame_iterator)
171
-
172
165
  @property
173
166
  def packet_iterator(
174
167
  self
@@ -179,16 +172,6 @@ class VideoReader:
179
172
  """
180
173
  return self.container.demux(self.video_stream)
181
174
 
182
- @property
183
- def next_packet(
184
- self
185
- ) -> Union[Packet, None]:
186
- """
187
- Get the next video packet (not decoded) from
188
- the iterator.
189
- """
190
- return next(self.packet_iterator)
191
-
192
175
  @property
193
176
  def audio_packet_iterator(
194
177
  self
@@ -199,16 +182,6 @@ class VideoReader:
199
182
  """
200
183
  return self.container.demux(self.audio_stream)
201
184
 
202
- @property
203
- def next_audio_packet(
204
- self
205
- ) -> Union[Packet, None]:
206
- """
207
- Get the next audio packet (not decoded) from
208
- the iterator.
209
- """
210
- return next(self.packet_iterator)
211
-
212
185
  @property
213
186
  def packet_with_audio_iterator(
214
187
  self
@@ -221,25 +194,37 @@ class VideoReader:
221
194
  return self.container.demux((self.video_stream, self.audio_stream))
222
195
 
223
196
  @property
224
- def next_packet_with_audio(
197
+ def has_video(
225
198
  self
226
- ) -> Union[Packet, None]:
199
+ ) -> bool:
227
200
  """
228
- Get the next video frames packet (or audio
229
- frames packet) from the iterator. Depending
230
- on the position, the packet can be video or
231
- audio.
201
+ Flag to indicate if there is a video stream
202
+ or not.
232
203
  """
233
- return next(self.packet_with_audio_iterator)
204
+ return self.video_stream is not None
205
+
206
+ @property
207
+ def has_audio(
208
+ self
209
+ ) -> bool:
210
+ """
211
+ Flag to indicate if there is an audio stream
212
+ or not.
213
+ """
214
+ return self.audio_stream is not None
234
215
 
235
216
  @property
236
217
  def codec_name(
237
218
  self
238
- ) -> str:
219
+ ) -> Union[str, None]:
239
220
  """
240
221
  Get the name of the video codec.
241
222
  """
242
- return self.video_stream.codec_context.name
223
+ return (
224
+ self.video_stream.codec_context.name
225
+ if self.has_video else
226
+ None
227
+ )
243
228
 
244
229
  @property
245
230
  def audio_codec_name(
@@ -248,64 +233,89 @@ class VideoReader:
248
233
  """
249
234
  Get the name of the audio codec.
250
235
  """
251
- return self.audio_stream.codec_context.name
236
+ return (
237
+ self.audio_stream.codec_context.name
238
+ if self.has_audio else
239
+ None
240
+ )
252
241
 
253
242
  @property
254
243
  def number_of_frames(
255
244
  self
256
- ) -> int:
245
+ ) -> Union[int, None]:
257
246
  """
258
247
  The number of frames in the video.
259
248
  """
260
- return self.video_stream.frames
249
+ return (
250
+ self.video_stream.frames
251
+ if self.has_video else
252
+ None
253
+ )
261
254
 
262
255
  @property
263
256
  def number_of_audio_frames(
264
257
  self
265
- ) -> int:
258
+ ) -> Union[int, None]:
266
259
  """
267
260
  The number of frames in the audio.
268
261
  """
269
- return self.audio_stream.frames
262
+ return (
263
+ self.audio_stream.frames
264
+ if self.has_audio else
265
+ None
266
+ )
270
267
 
271
268
  @property
272
269
  def fps(
273
270
  self
274
- ) -> Fraction:
271
+ ) -> Union[Fraction, None]:
275
272
  """
276
273
  The fps of the video.
277
274
  """
278
- # They return it as a Fraction but...
279
- return self.video_stream.average_rate
275
+ return (
276
+ self.video_stream.average_rate
277
+ if self.has_video else
278
+ None
279
+ )
280
280
 
281
281
  @property
282
282
  def audio_fps(
283
283
  self
284
- ) -> Fraction:
284
+ ) -> Union[int, None]:
285
285
  """
286
286
  The fps of the audio.
287
287
  """
288
- # TODO: What if no audio (?)
289
- return self.audio_stream.rate
288
+ return (
289
+ self.audio_stream.rate
290
+ if self.has_audio else
291
+ None
292
+ )
290
293
 
291
294
  @property
292
295
  def time_base(
293
296
  self
294
- ) -> Fraction:
297
+ ) -> Union[Fraction, None]:
295
298
  """
296
299
  The time base of the video.
297
300
  """
298
- return self.video_stream.time_base
301
+ return (
302
+ self.video_stream.time_base
303
+ if self.has_video else
304
+ None
305
+ )
299
306
 
300
307
  @property
301
308
  def audio_time_base(
302
309
  self
303
- ) -> Fraction:
310
+ ) -> Union[Fraction, None]:
304
311
  """
305
312
  The time base of the audio.
306
313
  """
307
- # TODO: What if no audio (?)
308
- return self.audio_stream.time_base
314
+ return (
315
+ self.audio_stream.time_base
316
+ if self.has_audio else
317
+ None
318
+ )
309
319
 
310
320
  @property
311
321
  def duration(
@@ -314,10 +324,15 @@ class VideoReader:
314
324
  """
315
325
  The duration of the video.
316
326
  """
327
+ # TODO: What to do in the case we have
328
+ # video stream but not 'duration'
329
+ # attribute (?)
317
330
  return (
318
331
  float(self.video_stream.duration * self.video_stream.time_base)
319
- if self.video_stream.duration else
320
- # TODO: What to do in this case (?)
332
+ if (
333
+ self.has_video and
334
+ self.video_stream.duration
335
+ ) else
321
336
  None
322
337
  )
323
338
 
@@ -328,43 +343,59 @@ class VideoReader:
328
343
  """
329
344
  The duration of the audio.
330
345
  """
331
- # TODO: What if no audio (?)
346
+ # TODO: What to do in the case we have
347
+ # audio stream but not 'duration'
348
+ # attribute (?)
332
349
  return (
333
350
  float(self.audio_stream.duration * self.audio_stream.time_base)
334
- if self.audio_stream.duration else
335
- # TODO: What to do in this case (?)
351
+ if (
352
+ self.has_audio and
353
+ self.audio_stream.duration
354
+ ) else
336
355
  None
337
356
  )
338
357
 
339
358
  @property
340
359
  def size(
341
360
  self
342
- ) -> tuple[int, int]:
361
+ ) -> Union[tuple[int, int], None]:
343
362
  """
344
363
  The size of the video in a (width, height) format.
345
364
  """
346
365
  return (
347
- self.video_stream.width,
348
- self.video_stream.height
366
+ (
367
+ self.video_stream.width,
368
+ self.video_stream.height
369
+ )
370
+ if self.has_video else
371
+ None
349
372
  )
350
373
 
351
374
  @property
352
375
  def width(
353
376
  self
354
- ) -> int:
377
+ ) -> Union[int, None]:
355
378
  """
356
379
  The width of the video, in pixels.
357
380
  """
358
- return self.size[0]
381
+ return (
382
+ self.size[0]
383
+ if self.size is not None else
384
+ None
385
+ )
359
386
 
360
387
  @property
361
388
  def height(
362
389
  self
363
- ) -> int:
390
+ ) -> Union[int, None]:
364
391
  """
365
392
  The height of the video, in pixels.
366
393
  """
367
- return self.size[1]
394
+ return (
395
+ self.size[1]
396
+ if self.size is not None else
397
+ None
398
+ )
368
399
 
369
400
  # Any property related to audio has to
370
401
  # start with 'audio_property_name'
@@ -389,14 +420,15 @@ class VideoReader:
389
420
  video (that also includes the audio) we
390
421
  are reading.
391
422
  """
392
- self.video_stream: VideoStream = None
423
+ self.video_stream: Union[VideoStream, None] = None
393
424
  """
394
- The stream that includes the video.
425
+ The stream that includes the video. If
426
+ no video stream this will be None.
395
427
  """
396
- # TODO: What if no audio (?)
397
- self.audio_stream: AudioStream = None
428
+ self.audio_stream: Union[AudioStream, None] = None
398
429
  """
399
- The stream that includes the audio.
430
+ The stream that includes the audio. If
431
+ no audio stream this will be None.
400
432
  """
401
433
  self.video_cache: VideoFrameCache = None
402
434
  """
@@ -434,22 +466,42 @@ class VideoReader:
434
466
  #self.container.close()
435
467
  else:
436
468
  self.container = av_open(self.filename)
469
+
470
+ self.video_stream = (
471
+ self.container.streams.video[0]
472
+ if self.container.streams.video else
473
+ None
474
+ )
437
475
  # TODO: Should this be 'AUTO' (?)
438
- self.video_stream = self.container.streams.video[0]
439
476
  self.video_stream.thread_type = 'AUTO'
440
- self.audio_stream = self.container.streams.audio[0]
477
+
478
+ self.audio_stream = (
479
+ self.container.streams.audio[0]
480
+ if self.container.streams.audio else
481
+ None
482
+ )
483
+ # TODO: Should this be 'AUTO' (?)
441
484
  self.audio_stream.thread_type = 'AUTO'
485
+
486
+ if (
487
+ self.video_stream is None and
488
+ self.audio_stream is None
489
+ ):
490
+ raise Exception(f'No video nor audio stream found in the "{self.filename}" file.')
491
+
442
492
  self.video_cache = VideoFrameCache(self.container, self.video_stream)
443
493
  self.audio_cache = VideoFrameCache(self.container, self.audio_stream)
444
494
 
445
495
  def seek(
446
496
  self,
447
- pts,
497
+ pts: int,
448
498
  stream = None
449
499
  ) -> 'VideoReader':
450
500
  """
451
501
  Call the container '.seek()' method with
452
- the given 'pts' packet time stamp.
502
+ the given 'pts' packet time stamp. By
503
+ default, the video stream is the one in
504
+ which we apply the seek.
453
505
  """
454
506
  stream = (
455
507
  self.video_stream
@@ -458,7 +510,10 @@ class VideoReader:
458
510
  )
459
511
 
460
512
  # TODO: Is 'offset' actually a 'pts' (?)
461
- self.container.seek(pts, stream = stream)
513
+ self.container.seek(
514
+ offset = pts,
515
+ stream = stream
516
+ )
462
517
 
463
518
  return self
464
519
 
@@ -472,7 +527,8 @@ class VideoReader:
472
527
  for frame in self.frame_iterator:
473
528
  yield VideoReaderFrame(
474
529
  frame = frame,
475
- t = float(frame.pts * self.time_base),
530
+ # TODO: Maybe use util to transform it (?)
531
+ t = frame.pts * self.time_base,
476
532
  pixel_format = self.pixel_format
477
533
  )
478
534
 
@@ -577,7 +633,7 @@ class VideoReader:
577
633
 
578
634
  def get_frame_from_t(
579
635
  self,
580
- t: float
636
+ t: Union[int, float, Fraction]
581
637
  ) -> 'VideoFrame':
582
638
  """
583
639
  Get the video frame with the given 't' time
@@ -585,21 +641,49 @@ class VideoReader:
585
641
  """
586
642
  return self.video_cache.get_frame_from_t(t)
587
643
 
588
- # TODO: Will we use this (?)
589
644
  def get_audio_frame(
590
645
  self,
591
646
  index: int
592
- ) -> 'VideoFrame':
647
+ ) -> 'AudioFrame':
593
648
  """
594
649
  Get the audio frame with the given 'index',
595
650
  using the audio cache system.
596
651
  """
597
- return self.video_cache.get_frame(index)
652
+ return self.audio_cache.get_frame(index)
653
+
654
+ def get_audio_frame_from_t(
655
+ self,
656
+ t: Union[int, float, Fraction]
657
+ ) -> 'AudioFrame':
658
+ """
659
+ Get the audio frame with the given 't' time
660
+ moment, using the audio cache system.
661
+ """
662
+ return self.audio_cache.get_frame_from_t(t)
663
+
664
+ def get_audio_frames_from_t(
665
+ self,
666
+ t: Union[int, float, Fraction]
667
+ ):
668
+ """
669
+ Get the sequence of audio frames for the
670
+ given video 't' time moment, using the
671
+ audio cache system.
672
+
673
+ This is useful when we want to write a
674
+ video frame with its audio, so we obtain
675
+ all the audio frames associated to it
676
+ (remember that a video frame is associated
677
+ with more than 1 audio frame).
678
+ """
679
+ t: T = T.from_fps(t, self.fps)
680
+ for frame in self.audio_cache.get_frames(t.truncated, t.next(1).truncated):
681
+ yield frame
598
682
 
599
683
  def get_frames(
600
684
  self,
601
- start: float = 0.0,
602
- end: Union[float, None] = None
685
+ start: Union[int, float, Fraction] = 0.0,
686
+ end: Union[int, float, Fraction, None] = None
603
687
  ):
604
688
  """
605
689
  Iterator to get the video frames in between
@@ -610,8 +694,8 @@ class VideoReader:
610
694
 
611
695
  def get_audio_frames(
612
696
  self,
613
- start: float = 0.0,
614
- end: Union[float, None] = None
697
+ start: Union[int, float, Fraction] = 0.0,
698
+ end: Union[int, float, Fraction, None] = None
615
699
  ):
616
700
  """
617
701
  Iterator to get the audio frames in between
@@ -629,6 +713,23 @@ class VideoReader:
629
713
  self.container.close()
630
714
 
631
715
 
716
+ # TODO: I think I'm not using this...
717
+ # Remove it please
718
+ def audio_ts_for_video_t(
719
+ t: float,
720
+ video_fps: float,
721
+ audio_fps: float
722
+ ):
723
+ # Remember, from [t_start, t_end), the last one
724
+ # is not included
725
+ audio_t_start = int(t * audio_fps)
726
+ audio_t_end = int((t + 1.0 / video_fps) * audio_fps)
727
+
728
+ return [
729
+ i / audio_fps
730
+ for i in range(audio_t_start, audio_t_end)
731
+ ]
732
+
632
733
 
633
734
 
634
735
  """