@@ -47,6 +47,7 @@ cdef class OutputContainer(Container):
4747 """ add_stream(codec_name, rate=None)
4848
4949 Creates a new stream from a codec name and returns it.
50+ Supports video, audio, and subtitle streams.
5051
5152 :param codec_name: The name of a codec.
5253 :type codec_name: str | Codec
@@ -137,7 +138,7 @@ cdef class OutputContainer(Container):
137138
138139 def add_stream_from_template (self , Stream template not None , **kwargs ):
139140 """
140- Creates a new stream from a template.
141+ Creates a new stream from a template. Supports video, audio, and subtitle streams.
141142
142143 :param template: Copy codec from another :class:`~av.stream.Stream` instance.
143144 :param \\ **kwargs: Set attributes for the stream.
@@ -192,6 +193,65 @@ cdef class OutputContainer(Container):
192193
193194 return py_stream
194195
196+
197+ def add_data_stream (self , codec_name = None , dict options = None ):
198+ """ add_data_stream(codec_name=None)
199+
200+ Creates a new data stream and returns it.
201+
202+ :param codec_name: Optional name of the data codec (e.g. 'klv')
203+ :type codec_name: str | None
204+ :param dict options: Stream options.
205+ :rtype: The new :class:`~av.data.stream.DataStream`.
206+ """
207+ cdef const lib.AVCodec * codec = NULL
208+
209+ if codec_name is not None :
210+ codec = lib.avcodec_find_encoder_by_name(codec_name.encode())
211+ if codec == NULL :
212+ raise ValueError (f" Unknown data codec: {codec_name}" )
213+
214+ # Assert that this format supports the requested codec
215+ if not lib.avformat_query_codec(self .ptr.oformat, codec.id, lib.FF_COMPLIANCE_NORMAL):
216+ raise ValueError (
217+ f" {self.format.name!r} format does not support {codec_name!r} codec"
218+ )
219+
220+ # Create new stream in the AVFormatContext
221+ cdef lib.AVStream * stream = lib.avformat_new_stream(self .ptr, codec)
222+ if stream == NULL :
223+ raise MemoryError (" Could not allocate stream" )
224+
225+ # Set up codec context if we have a codec
226+ cdef lib.AVCodecContext * codec_context = NULL
227+ if codec != NULL :
228+ codec_context = lib.avcodec_alloc_context3(codec)
229+ if codec_context == NULL :
230+ raise MemoryError (" Could not allocate codec context" )
231+
232+ # Some formats want stream headers to be separate
233+ if self .ptr.oformat.flags & lib.AVFMT_GLOBALHEADER:
234+ codec_context.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER
235+
236+ # Initialize stream codec parameters
237+ err_check(lib.avcodec_parameters_from_context(stream.codecpar, codec_context))
238+ else :
239+ # For raw data streams, just set the codec type
240+ stream.codecpar.codec_type = lib.AVMEDIA_TYPE_DATA
241+
242+ # Construct the user-land stream
243+ cdef CodecContext py_codec_context = None
244+ if codec_context != NULL :
245+ py_codec_context = wrap_codec_context(codec_context, codec)
246+
247+ cdef Stream py_stream = wrap_stream(self , stream, py_codec_context)
248+ self .streams.add_stream(py_stream)
249+
250+ if options:
251+ py_stream.options.update(options)
252+
253+ return py_stream
254+
195255 cpdef start_encoding(self ):
196256 """ Write the file header! Called automatically."""
197257
@@ -206,8 +266,11 @@ cdef class OutputContainer(Container):
206266 cdef Stream stream
207267 for stream in self .streams:
208268 ctx = stream.codec_context
269+ # Skip codec context handling for data streams without codecs
209270 if ctx is None :
210- raise ValueError (f" Stream {stream.index} has no codec context" )
271+ if stream.type != " data" :
272+ raise ValueError (f" Stream {stream.index} has no codec context" )
273+ continue
211274
212275 if not ctx.is_open:
213276 for k, v in self .options.items():
0 commit comments