diff --git a/av/container/core.pyx b/av/container/core.pyx index d21893c43..98f03817a 100755 --- a/av/container/core.pyx +++ b/av/container/core.pyx @@ -168,13 +168,52 @@ Flags = define_enum('Flags', __name__, ( ), is_flags=True) +AudioCodecs = define_enum('AudioCodecs', __name__, ( + ('NONE', lib.AV_CODEC_ID_NONE), + ('PCM_ALAW', lib.AV_CODEC_ID_PCM_ALAW), + ('PCM_BLURAY', lib.AV_CODEC_ID_PCM_BLURAY), + ('PCM_DVD', lib.AV_CODEC_ID_PCM_DVD), + ('PCM_F16LE', lib.AV_CODEC_ID_PCM_F16LE), + ('PCM_F24LE', lib.AV_CODEC_ID_PCM_F24LE), + ('PCM_F32BE', lib.AV_CODEC_ID_PCM_F32BE), + ('PCM_F32LE', lib.AV_CODEC_ID_PCM_F32LE), + ('PCM_F64BE', lib.AV_CODEC_ID_PCM_F64BE), + ('PCM_F64LE', lib.AV_CODEC_ID_PCM_F64LE), + ('PCM_LXF', lib.AV_CODEC_ID_PCM_LXF), + ('PCM_MULAW', lib.AV_CODEC_ID_PCM_MULAW), + ('PCM_S16BE', lib.AV_CODEC_ID_PCM_S16BE), + ('PCM_S16BE_PLANAR', lib.AV_CODEC_ID_PCM_S16BE_PLANAR), + ('PCM_S16LE', lib.AV_CODEC_ID_PCM_S16LE), + ('PCM_S16LE_PLANAR', lib.AV_CODEC_ID_PCM_S16LE_PLANAR), + ('PCM_S24BE', lib.AV_CODEC_ID_PCM_S24BE), + ('PCM_S24DAUD', lib.AV_CODEC_ID_PCM_S24DAUD), + ('PCM_S24LE', lib.AV_CODEC_ID_PCM_S24LE), + ('PCM_S24LE_PLANAR', lib.AV_CODEC_ID_PCM_S24LE_PLANAR), + ('PCM_S32BE', lib.AV_CODEC_ID_PCM_S32BE), + ('PCM_S32LE', lib.AV_CODEC_ID_PCM_S32LE), + ('PCM_S32LE_PLANAR', lib.AV_CODEC_ID_PCM_S32LE_PLANAR), + ('PCM_S64BE', lib.AV_CODEC_ID_PCM_S64BE), + ('PCM_S64LE', lib.AV_CODEC_ID_PCM_S64LE), + ('PCM_S8', lib.AV_CODEC_ID_PCM_S8), + ('PCM_S8_PLANAR', lib.AV_CODEC_ID_PCM_S8_PLANAR), + ('PCM_U16BE', lib.AV_CODEC_ID_PCM_U16BE), + ('PCM_U16LE', lib.AV_CODEC_ID_PCM_U16LE), + ('PCM_U24BE', lib.AV_CODEC_ID_PCM_U24BE), + ('PCM_U24LE', lib.AV_CODEC_ID_PCM_U24LE), + ('PCM_U32BE', lib.AV_CODEC_ID_PCM_U32BE), + ('PCM_U32LE', lib.AV_CODEC_ID_PCM_U32LE), + ('PCM_U8', lib.AV_CODEC_ID_PCM_U8), + ('PCM_VIDC', lib.AV_CODEC_ID_PCM_VIDC) +)) + + cdef class Container(object): def __cinit__(self, sentinel, file_, format_name, options, container_options, stream_options, metadata_encoding, metadata_errors, buffer_size, open_timeout, read_timeout, - io_open): + io_open, audio_codec=None): if sentinel is not _cinit_sentinel: raise RuntimeError('cannot construct base Container') @@ -240,6 +279,9 @@ cdef class Container(object): self.ptr.flags |= lib.AVFMT_FLAG_GENPTS self.ptr.opaque = self + if audio_codec is not None: + self.ptr.audio_codec_id = AudioCodecs[audio_codec] + # Setup Python IO. self.open_files = {} if not isinstance(file_, basestring): @@ -338,7 +380,8 @@ cdef class Container(object): def open(file, mode=None, format=None, options=None, container_options=None, stream_options=None, metadata_encoding='utf-8', metadata_errors='strict', - buffer_size=32768, timeout=None, io_open=None): + buffer_size=32768, timeout=None, io_open=None, + audio_codec=None): """open(file, mode='r', **kwargs) Main entrypoint to opening files/streams. @@ -365,6 +408,10 @@ def open(file, mode=None, format=None, options=None, ``url`` is the url to open, ``flags`` is a combination of AVIO_FLAG_* and ``options`` is a dictionary of additional options. The callable should return a file-like object. + :param str audio_codec: Specify audio input codec to be used when reading + form ALSA or PulseAudio devices. For example ``"PCM_S32LE"`` or + ``"PCM_S16BE"``. If not given, the codec is left unconfigured and + defaults to FFmpeg default. For devices (via ``libavdevice``), pass the name of the device to ``format``, e.g.:: @@ -397,13 +444,19 @@ def open(file, mode=None, format=None, options=None, open_timeout = timeout read_timeout = timeout + if audio_codec is not None: + try: + AudioCodecs[audio_codec] + except KeyError: + raise ValueError("Provided audio_codec must be one of the supported audio codecs") + if mode.startswith('r'): return InputContainer( _cinit_sentinel, file, format, options, container_options, stream_options, metadata_encoding, metadata_errors, buffer_size, open_timeout, read_timeout, - io_open + io_open, audio_codec ) if mode.startswith('w'): if stream_options: diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index 1e6111808..e24224f7c 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -115,6 +115,41 @@ cdef extern from "libavcodec/avcodec.h" nogil: AV_CODEC_ID_NONE AV_CODEC_ID_MPEG2VIDEO AV_CODEC_ID_MPEG1VIDEO + # Audio codecs. + AV_CODEC_ID_PCM_ALAW + AV_CODEC_ID_PCM_BLURAY + AV_CODEC_ID_PCM_DVD + AV_CODEC_ID_PCM_F16LE + AV_CODEC_ID_PCM_F24LE + AV_CODEC_ID_PCM_F32BE + AV_CODEC_ID_PCM_F32LE + AV_CODEC_ID_PCM_F64BE + AV_CODEC_ID_PCM_F64LE + AV_CODEC_ID_PCM_LXF + AV_CODEC_ID_PCM_MULAW + AV_CODEC_ID_PCM_S16BE + AV_CODEC_ID_PCM_S16BE_PLANAR + AV_CODEC_ID_PCM_S16LE + AV_CODEC_ID_PCM_S16LE_PLANAR + AV_CODEC_ID_PCM_S24BE + AV_CODEC_ID_PCM_S24DAUD + AV_CODEC_ID_PCM_S24LE + AV_CODEC_ID_PCM_S24LE_PLANAR + AV_CODEC_ID_PCM_S32BE + AV_CODEC_ID_PCM_S32LE + AV_CODEC_ID_PCM_S32LE_PLANAR + AV_CODEC_ID_PCM_S64BE + AV_CODEC_ID_PCM_S64LE + AV_CODEC_ID_PCM_S8 + AV_CODEC_ID_PCM_S8_PLANAR + AV_CODEC_ID_PCM_U16BE + AV_CODEC_ID_PCM_U16LE + AV_CODEC_ID_PCM_U24BE + AV_CODEC_ID_PCM_U24LE + AV_CODEC_ID_PCM_U32BE + AV_CODEC_ID_PCM_U32LE + AV_CODEC_ID_PCM_U8 + AV_CODEC_ID_PCM_VIDC cdef enum AVDiscard: AVDISCARD_NONE diff --git a/include/libavformat/avformat.pxd b/include/libavformat/avformat.pxd index ed3e503f5..82e3ace43 100644 --- a/include/libavformat/avformat.pxd +++ b/include/libavformat/avformat.pxd @@ -177,6 +177,9 @@ cdef extern from "libavformat/avformat.h" nogil: AVDictionary *metadata + # Forced audio codec set by user. + AVCodecID audio_codec_id + char filename int64_t start_time int64_t duration diff --git a/tests/test_errors.py b/tests/test_errors.py index 55d969999..7cb1add08 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -67,3 +67,11 @@ def test_buffertoosmall(self): self.assertEqual(e.errno, av.error.BUFFER_TOO_SMALL.value) else: self.fail("no exception raised") + + def test_not_supported_audio_codec(self): + try: + av.open("alsa_device", audio_codec="does_not_exist") + except ValueError as e: + self.assertEqual(str(e), "Provided audio_codec must be one of the supported audio codecs") + else: + self.fail("no exception raised")