Skip to content
GitLab
    • Explore Projects Groups Snippets
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • P PyAV
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 37
    • Issues 37
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 26
    • Merge requests 26
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • PyAV
  • PyAV
  • Merge requests
  • !566
An error occurred while fetching the assigned milestone of the selected merge_request.

Add VideoFrame.save(path, ...) method.

  • Review changes

  • Download
  • Email patches
  • Plain diff
Open Mike Boers requested to merge frame_save into develop 5 years ago
  • Overview 0
  • Commits 4
  • Pipelines 0
  • Changes 5
Compare
  • develop (base)

and
  • latest version
    95254d6c
    4 commits, 2 years ago

5 files
+ 74
- 2

    Preferences

    File browser
    Compare changes
a‎v‎
co‎dec‎
code‎c.pyx‎ +3 -0
vi‎deo‎
forma‎t.pyx‎ +10 -0
fram‎e.pyx‎ +45 -0
example‎s/basics‎
save_key‎frames.py‎ +3 -2
te‎sts‎
test_vide‎oformat.py‎ +13 -0
av/codec/codec.pyx
+ 3
- 0
  • View file @ 95254d6c

  • Edit in single-file editor

  • Open in Web IDE


@@ -86,6 +86,9 @@ cdef class Codec(object):
if self.is_encoder and lib.av_codec_is_decoder(self.ptr):
raise RuntimeError('%s is both encoder and decoder.')
def __repr__(self):
return '<av.Codec {!r} mode={!r} at 0x{:x}>'.format(self.name, 'w' if self.is_encoder else 'r', id(self))
def create(self):
from .context import CodecContext
return CodecContext.create(self)
av/video/format.pyx
+ 10
- 0
  • View file @ 95254d6c

  • Edit in single-file editor

  • Open in Web IDE


@@ -53,6 +53,16 @@ cdef class VideoFormat(object):
def __int__(self):
return int(self.pix_fmt)
def __eq__(self, other):
if isinstance(other, VideoFormat):
return (
self.pix_fmt == int(other) and
self.width == other.width and
self.height == other.height
)
else:
raise TypeError("Cannot compare VideoFormat to {}.".format(type(other)))
property name:
"""Canonical name of the pixel format."""
def __get__(self):
av/video/frame.pyx
+ 45
- 0
  • View file @ 95254d6c

  • Edit in single-file editor

  • Open in Web IDE


from libc.stdint cimport uint8_t
from av.codec.codec cimport Codec
from av.codec.context cimport CodecContext
from av.deprecation import renamed_attr
from av.enums cimport define_enum
from av.error cimport err_check
from av.video.format cimport get_video_format, VideoFormat
from av.video.plane cimport VideoPlane
import os
cdef object _cinit_bypass_sentinel
@@ -330,6 +334,47 @@ cdef class VideoFrame(Frame):
"""
return self.reformat(format="rgb24", **kwargs)
def save(self, path, codec=None, format=None, options=None):
if not codec:
ext = os.path.splitext(path)[1].lower()
if not ext:
raise ValueError("Please provide a codec or a path with an extension.")
codec_name = {
'.jpg': 'mjpeg',
'.jpeg': 'mjpeg',
'.tif': 'tiff',
}.get(ext, ext[1:])
codec = Codec(codec_name, 'w')
elif not isinstance(codec, Codec):
codec = Codec(codec, 'w')
given_format = format is not None
format = VideoFormat(format or self.format)
if not any(format.name == f.name for f in codec.video_formats):
if given_format:
raise ValueError("Format {!r} is not in codec.video_formats.".format(format))
format = codec.video_formats[0]
to_encode = self.reformat(format=format)
ctx = codec.create()
ctx.width = self.width
ctx.height = self.height
ctx.pix_fmt = format.name
if options:
ctx.options.update(options)
ctx.open()
packets = ctx.encode(to_encode)
if len(packets) != 1:
raise ValueError("Frame encoded to {} packets; expected 1.".format(len(packets)))
with open(path, 'wb') as fh:
fh.write(packets[0])
def to_image(self, **kwargs):
"""Get an RGB ``PIL.Image`` of this frame.
examples/basics/save_keyframes.py
+ 3
- 2
  • View file @ 95254d6c

  • Edit in single-file editor

  • Open in Web IDE


Conflict: This file was modified in both the source and target branches. Ask someone with write access to resolve it.
@@ -14,7 +14,8 @@ for frame in container.decode(stream):
print(frame)
# We use `frame.pts` as `frame.index` won't make must sense with the `skip_frame`.
frame.to_image().save(
frame.save(
'night-sky.{:04d}.jpg'.format(frame.pts),
quality=80,
# format='yuv420p'
# quality=80,
)
tests/test_videoformat.py
+ 13
- 0
  • View file @ 95254d6c

  • Edit in single-file editor

  • Open in Web IDE


@@ -88,3 +88,16 @@ class TestVideoFormats(TestCase):
fmt = VideoFormat('pal8', 640, 480)
self.assertEqual(len(fmt.components), 1)
self.assertTrue(fmt.has_palette)
def test_equality(self):
a = VideoFormat('yuv420p')
b = VideoFormat('yuv420p')
c = VideoFormat('rgb24')
self.assertIsNot(a, b)
self.assertEqual(a, b)
self.assertNotEqual(a, c)
self.assertIn(a, [b, c])
self.assertNotIn(a, [c])
0 Assignees
None
Assign to
0 Reviewers
None
Request review from
Labels
0
None
0
None
    Assign labels
  • Manage project labels

Milestone
No milestone
None
None
Time tracking
No estimate or time spent
Lock merge request
Unlocked
0
0 participants
Reference:
Source branch: frame_save

Menu

Explore Projects Groups Snippets