Skip to content

Commit

Permalink
Add stream.disposition
Browse files Browse the repository at this point in the history
  • Loading branch information
WyattBlue committed Jan 19, 2025
1 parent 8b10ef8 commit 9369115
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 61 deletions.
56 changes: 0 additions & 56 deletions av/container/output.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ from fractions import Fraction

cimport libav as lib

from libc.string cimport memcpy
from libc.stdint cimport uint8_t

from av.codec.codec cimport Codec
from av.codec.context cimport CodecContext, wrap_codec_context
from av.container.streams cimport StreamContainer
Expand Down Expand Up @@ -251,59 +248,6 @@ cdef class OutputContainer(Container):
return py_stream


def add_attachment(self, file_data, filename, mimetype=None):
"""Add a file as an attachment stream."""
cdef lib.AVStream *stream
cdef size_t data_len
cdef uint8_t *attachment
cdef Stream py_stream

if not isinstance(file_data, bytes):
raise TypeError("file_data must be bytes")

if not filename:
raise ValueError("filename must be provided")

# Create new stream without codec
stream = lib.avformat_new_stream(self.ptr, NULL)
if stream == NULL:
raise MemoryError("Could not allocate stream")

try:
# Mark as attachment type
stream.codecpar.codec_type = lib.AVMEDIA_TYPE_ATTACHMENT

# Allocate and copy attachment data
data_len = len(file_data)
attachment = <uint8_t*>lib.av_malloc(data_len)
if attachment == NULL:
raise MemoryError("Could not allocate attachment buffer")

# Copy the data
memcpy(attachment, <uint8_t*>file_data, data_len)

# Store attachment in codecpar extradata
stream.codecpar.extradata = attachment
stream.codecpar.extradata_size = data_len

# Add metadata
err_check(lib.av_dict_set(&stream.metadata, "filename", filename.encode('utf-8'), 0))
err_check(lib.av_dict_set(&stream.metadata, "mimetype", mimetype.encode('utf-8'), 0))

# Explicitly set time_base to avoid duration issues
stream.time_base.num = 1
stream.time_base.den = 1

# Create Python stream object
py_stream = wrap_stream(self, stream, None)
self.streams.add_stream(py_stream)

return py_stream

except Exception:
lib.av_free(attachment)
raise

cpdef start_encoding(self):
"""Write the file header! Called automatically."""

Expand Down
23 changes: 23 additions & 0 deletions av/stream.pyi
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
from enum import Flag
from fractions import Fraction
from typing import Literal

from .codec import Codec, CodecContext
from .container import Container

class Disposition(Flag):
default: int
dub: int
original: int
comment: int
lyrics: int
karaoke: int
forced: int
hearing_impaired: int
visual_impaired: int
clean_effects: int
attached_pic: int
timed_thumbnails: int
non_diegetic: int
captions: int
descriptions: int
metadata: int
dependent: int
still_image: int
multilayer: int

class Stream:
name: str | None
container: Container
Expand All @@ -20,6 +42,7 @@ class Stream:
guessed_rate: Fraction | None
start_time: int | None
duration: int | None
disposition: Disposition
frames: int
language: str | None
type: Literal["video", "audio", "data", "subtitle", "attachment"]
31 changes: 30 additions & 1 deletion av/stream.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cimport libav as lib

from enum import Enum
from enum import Flag

from av.error cimport err_check
from av.packet cimport Packet
Expand All @@ -12,6 +12,28 @@ from av.utils cimport (
)


class Disposition(Flag):
default = 1 << 0
dub = 1 << 1
original = 1 << 2
comment = 1 << 3
lyrics = 1 << 4
karaoke = 1 << 5
forced = 1 << 6
hearing_impaired = 1 << 7
visual_impaired = 1 << 8
clean_effects = 1 << 9
attached_pic = 1 << 10
timed_thumbnails = 1 << 11
non_diegetic = 1 << 12
captions = 1 << 16
descriptions = 1 << 17
metadata = 1 << 18
dependent = 1 << 19
still_image = 1 << 20
multilayer = 1 << 21


cdef object _cinit_bypass_sentinel = object()

cdef Stream wrap_stream(Container container, lib.AVStream *c_stream, CodecContext codec_context):
Expand Down Expand Up @@ -96,6 +118,9 @@ cdef class Stream:
if name == "id":
self._set_id(value)
return
if name == "disposition":
self.ptr.disposition = value
return

# Convenience setter for codec context properties.
if self.codec_context is not None:
Expand Down Expand Up @@ -230,6 +255,10 @@ cdef class Stream:
"""
return self.metadata.get("language")

@property
def disposition(self):
return Disposition(self.ptr.disposition)

@property
def type(self):
"""
Expand Down
4 changes: 0 additions & 4 deletions include/libav.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ include "libavfilter/buffersink.pxd"
include "libavfilter/buffersrc.pxd"


cdef extern from "libavutil/mem.h":
void* av_malloc(size_t size) nogil
void av_free(void* ptr) nogil

cdef extern from "stdio.h" nogil:
cdef int snprintf(char *output, int n, const char *format, ...)
cdef int vsnprintf(char *output, int n, const char *format, va_list args)
2 changes: 2 additions & 0 deletions tests/test_colorspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def test_sky_timelapse() -> None:
)
stream = container.streams.video[0]

assert stream.disposition == av.stream.Disposition.default

assert stream.codec_context.color_range == 1
assert stream.codec_context.color_range == ColorRange.MPEG
assert stream.codec_context.color_primaries == 1
Expand Down

0 comments on commit 9369115

Please sign in to comment.