add opus/webm encoding (#22923)

This commit is contained in:
Christian Baars 2025-02-02 21:49:11 +01:00 committed by GitHub
parent e8ca96008f
commit def7ede895
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 18018 additions and 421 deletions

View File

@ -171,7 +171,7 @@ extern "C" {
#define OPUS_GET_IN_DTX_REQUEST 4049
/** Defines for the presence of extended APIs. */
#define OPUS_HAVE_OPUS_PROJECTION_H
// #define OPUS_HAVE_OPUS_PROJECTION_H
/* Macros to trigger compilation errors when the wrong types are provided to a CTL */
#define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))

View File

@ -0,0 +1,35 @@
---
Language: Cpp
BasedOnStyle: Google
AlignTrailingComments: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
# A separate 'Other libraries' grouping is added before libwebm's headers for
# gtest and gmock includes. This is based on the suggested grouping in the
# Google C++ Style Guide:
# https://google.github.io/styleguide/cppguide.html#Names_and_Order_of_Includes
# The other categories come from `clang-format-14 --dump-config --style=Google`.
# See the clang-format documentation for more information on this option:
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#includecategories
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^((<|")(gtest|gmock)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 4
SortPriority: 0
CaseSensitive: false

View File

@ -0,0 +1,6 @@
*.sln eol=crlf
*.vcproj eol=crlf
*.vsprops eol=crlf
*.vcxproj eol=crlf
*.mkv -text -diff
*.webm -text -diff

View File

@ -0,0 +1,36 @@
*.MKV
*.a
*.cmake
*.d
*.exe
*.mkv
*.ncb
*.o
*.opensdf
*.sdf
*.so*
*.suo
*.swp
*.user
*~
.vscode
/*.webm
CMakeCache.txt
CMakeFiles
Debug
Makefile
Release
core
dumpvtt
ipch
mkvmuxer_sample
mkvmuxer_tests
mkvparser_sample
mkvparser_tests
vp9_header_parser_tests
vp9_level_stats_tests
vttdemux
webm2pes
webm2pes_tests
webm2ts
webm_info

View File

@ -0,0 +1,6 @@
Hui Su <huisu@google.com>
Matthew Heaney <matthewjheaney@google.com>
Neil Birkbeck <birkbeck@google.com>
Patrik Carlsson <patrik2.carlsson@sonymobile.com>
Roberto Alanis Baez <alanisbaez@google.com>
Tom Finegan <tomfinegan@google.com> <tomfinegan@chromium.org>

View File

@ -0,0 +1,441 @@
# This Pylint rcfile contains a best-effort configuration to uphold the
# best-practices and style described in the Google Python style guide:
# https://google.github.io/styleguide/pyguide.html
#
# Its canonical open-source location is:
# https://google.github.io/styleguide/pylintrc
[MASTER]
# Files or directories to be skipped. They should be base names, not paths.
ignore=third_party
# Files or directories matching the regex patterns are skipped. The regex
# matches against base names, not paths.
ignore-patterns=
# Pickle collected data for later comparisons.
persistent=no
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Use multiple processes to speed up Pylint.
jobs=4
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=abstract-method,
apply-builtin,
arguments-differ,
attribute-defined-outside-init,
backtick,
bad-option-value,
basestring-builtin,
buffer-builtin,
c-extension-no-member,
consider-using-enumerate,
cmp-builtin,
cmp-method,
coerce-builtin,
coerce-method,
delslice-method,
div-method,
duplicate-code,
eq-without-hash,
execfile-builtin,
file-builtin,
filter-builtin-not-iterating,
fixme,
getslice-method,
global-statement,
hex-method,
idiv-method,
implicit-str-concat-in-sequence,
import-error,
import-self,
import-star-module-level,
inconsistent-return-statements,
input-builtin,
intern-builtin,
invalid-str-codec,
locally-disabled,
long-builtin,
long-suffix,
map-builtin-not-iterating,
misplaced-comparison-constant,
missing-function-docstring,
metaclass-assignment,
next-method-called,
next-method-defined,
no-absolute-import,
no-else-break,
no-else-continue,
no-else-raise,
no-else-return,
no-init, # added
no-member,
no-name-in-module,
no-self-use,
nonzero-method,
oct-method,
old-division,
old-ne-operator,
old-octal-literal,
old-raise-syntax,
parameter-unpacking,
print-statement,
raising-string,
range-builtin-not-iterating,
raw_input-builtin,
rdiv-method,
reduce-builtin,
relative-import,
reload-builtin,
round-builtin,
setslice-method,
signature-differs,
standarderror-builtin,
suppressed-message,
sys-max-int,
too-few-public-methods,
too-many-ancestors,
too-many-arguments,
too-many-boolean-expressions,
too-many-branches,
too-many-instance-attributes,
too-many-locals,
too-many-nested-blocks,
too-many-public-methods,
too-many-return-statements,
too-many-statements,
trailing-newlines,
unichr-builtin,
unicode-builtin,
unnecessary-pass,
unpacking-in-except,
useless-else-on-loop,
useless-object-inheritance,
useless-suppression,
using-cmp-argument,
wrong-import-order,
xrange-builtin,
zip-builtin-not-iterating,
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]". This option is deprecated
# and it will be removed in Pylint 2.0.
files-output=no
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
[BASIC]
# Good variable names which should always be accepted, separated by a comma
good-names=main,_,PRESUBMIT
# Bad variable names which should always be refused, separated by a comma
bad-names=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
# Regular expression matching correct function names
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=^[a-z][a-z0-9_]*$
# Regular expression matching correct constant names
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
# Regular expression matching correct attribute names
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
# Regular expression matching correct argument names
argument-rgx=^[a-z][a-z0-9_]*$
# Regular expression matching correct class attribute names
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
# Regular expression matching correct inline iteration names
inlinevar-rgx=^[a-z][a-z0-9_]*$
# Regular expression matching correct class names
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
# Regular expression matching correct module names
module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
# Regular expression matching correct method names
method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=10
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=80
# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
# lines made too long by directives to pytype.
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=(?x)(
^\s*(\#\ )?<?https?://\S+>?$|
^\s*(from\s+\S+\s+)?import\s+.+$)
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=yes
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=
# Maximum number of lines in a module
max-module-lines=99999
# String used as indentation unit. The internal Google style guide mandates 2
# spaces. Google's externaly-published style guide says 4, consistent with
# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
# projects (like TensorFlow).
indent-string=' '
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=TODO
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=yes
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging,absl.logging,tensorflow.io.logging
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,
TERMIOS,
Bastion,
rexec,
sets
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant, absl
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls,
class_
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=StandardError,
Exception,
BaseException

View File

@ -0,0 +1,5 @@
# Names should be added to this file like so:
# Name or Organization <email address>
Google Inc.
Elijah Cirioli <eli.cirioli@gmail.com>

View File

@ -0,0 +1,41 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use a [Gerrit](https://www.gerritcodereview.com) instance hosted at
https://chromium-review.googlesource.com for this purpose. See the
[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/)
for additional details.
## Code Style
The C++ code style is based on the
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and
`clang-format --style=Google`. `clang-format -i --style=file` can be used to
format individual files, it will use the settings from `.clang-format`.
CMake files are formatted with
[cmake-format](https://cmake-format.readthedocs.io/en/latest/). `cmake-format
-i` can be used to format individual files, it will use the settings from
`.cmake-format.py`.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).

View File

@ -0,0 +1,30 @@
Copyright (c) 2010, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Google nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,23 @@
Additional IP Rights Grant (Patents)
------------------------------------
"These implementations" means the copyrightable works that implement the WebM
codecs distributed by Google as part of the WebM Project.
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable (except as stated in this section) patent license to
make, have made, use, offer to sell, sell, import, transfer, and otherwise
run, modify and propagate the contents of these implementations of WebM, where
such license applies only to those patent claims, both currently owned by
Google and acquired in the future, licensable by Google that are necessarily
infringed by these implementations of WebM. This grant does not include claims
that would be infringed only as a consequence of further modification of these
implementations. If you or your agent or exclusive licensee institute or order
or agree to the institution of patent litigation or any other patent
enforcement activity against any entity (including a cross-claim or
counterclaim in a lawsuit) alleging that any of these implementations of WebM
or any code incorporated within any of these implementations of WebM
constitute direct or contributory patent infringement, or inducement of
patent infringement, then any patent rights granted to you under this License
for these implementations of WebM shall terminate as of the date such
litigation is filed.

View File

@ -0,0 +1,202 @@
# Copyright (c) 2021, Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# * Neither the name of Google nor the names of its contributors may
# be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Top-level presubmit script for libwebm.
See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
details on the presubmit API built into depot_tools.
"""
import re
import subprocess2
USE_PYTHON3 = True
_BASH_INDENTATION = "2"
_GIT_COMMIT_SUBJECT_LENGTH = 65
_INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"]
_INCLUDE_SOURCE_FILES_ONLY = [r".*\.(c|cc|[hc]pp|h)$"]
_LIBWEBM_MAX_LINE_LENGTH = 80
def _CheckCommitSubjectLength(input_api, output_api):
"""Ensures commit's subject length is no longer than 65 chars."""
name = "git-commit subject"
cmd = ["git", "log", "-1", "--pretty=%s"]
start = input_api.time.time()
proc = subprocess2.Popen(
cmd,
stderr=subprocess2.PIPE,
stdout=subprocess2.PIPE,
universal_newlines=True)
stdout, _ = proc.communicate()
duration = input_api.time.time() - start
if not re.match(r"^Revert",
stdout) and (len(stdout) - 1) > _GIT_COMMIT_SUBJECT_LENGTH:
failure_msg = (
"The commit subject: %s is too long (%d chars)\n"
"Try to keep this to 50 or less (up to 65 is permitted for "
"non-reverts).\n"
"https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-"
"Project#_commit_guidelines") % (stdout, len(stdout) - 1)
return output_api.PresubmitError("%s\n (%4.2fs) failed\n%s" %
(name, duration, failure_msg))
return output_api.PresubmitResult("%s\n (%4.2fs) success" % (name, duration))
def _GetFilesToSkip(input_api):
"""Skips libwebm-specific files."""
return list(input_api.DEFAULT_FILES_TO_SKIP) + [
r"\.pylintrc$",
]
def _CheckChangeLintsClean(input_api, output_api):
"""Makes sure that libwebm/ code is cpplint clean."""
sources = lambda x: input_api.FilterSourceFile(
x, files_to_check=_INCLUDE_SOURCE_FILES_ONLY, files_to_skip=None)
return input_api.canned_checks.CheckChangeLintsClean(input_api, output_api,
sources)
def _RunShellCheckCmd(input_api, output_api, bash_file):
"""shellcheck command wrapper."""
cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file]
name = "Check %s file." % bash_file
start = input_api.time.time()
output, rc = subprocess2.communicate(
cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
duration = input_api.time.time() - start
if rc == 0:
return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
(name, " ".join(cmd), duration))
return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
(name, " ".join(cmd), duration, output[1]))
def _RunShfmtCheckCmd(input_api, output_api, bash_file):
"""shfmt command wrapper."""
cmd = [
"shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d",
bash_file
]
name = "Check %s file." % bash_file
start = input_api.time.time()
output, rc = subprocess2.communicate(
cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
duration = input_api.time.time() - start
if rc == 0:
return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
(name, " ".join(cmd), duration))
return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
(name, " ".join(cmd), duration, output[1]))
def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check):
"""Ensure that libwebm/ files are clean."""
file_filter = lambda x: input_api.FilterSourceFile(
x, files_to_check=files_to_check, files_to_skip=None)
affected_files = input_api.change.AffectedFiles(file_filter=file_filter)
results = [
run_cmd(input_api, output_api, f.AbsoluteLocalPath())
for f in affected_files
]
return results
def _CommonChecks(input_api, output_api):
results = []
results.extend(
input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
input_api, output_api))
results.extend(
input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api))
results.extend(
input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
input_api, output_api))
results.append(_CheckCommitSubjectLength(input_api, output_api))
source_file_filter = lambda x: input_api.FilterSourceFile(
x, files_to_skip=_GetFilesToSkip(input_api))
results.extend(
input_api.canned_checks.CheckLongLines(
input_api,
output_api,
maxlen=_LIBWEBM_MAX_LINE_LENGTH,
source_file_filter=source_file_filter))
results.extend(
input_api.canned_checks.CheckPatchFormatted(
input_api,
output_api,
check_clang_format=True,
check_python=True,
result_factory=output_api.PresubmitError))
results.extend(_CheckChangeLintsClean(input_api, output_api))
# Run pylint.
results.extend(
input_api.canned_checks.RunPylint(
input_api,
output_api,
files_to_skip=_GetFilesToSkip(input_api),
pylintrc=".pylintrc",
version="2.7"))
# Binaries shellcheck and shfmt are not installed in depot_tools.
# Installation is needed
try:
subprocess2.communicate(["shellcheck", "--version"])
results.extend(
_RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd,
_INCLUDE_BASH_FILES_ONLY))
print("shfmt")
subprocess2.communicate(["shfmt", "-version"])
results.extend(
_RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd,
_INCLUDE_BASH_FILES_ONLY))
except OSError as os_error:
results.append(
output_api.PresubmitPromptWarning(
"%s\nPlease install missing binaries locally." % os_error.args[0]))
return results
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
return results

View File

@ -0,0 +1,148 @@
Building Libwebm
To build libwebm you must first create project files. To do this run cmake
and pass it the path to your libwebm repo.
Makefile.unix can be used as a fallback on systems that cmake does not
support.
CMake Basics
To generate project/make files for the default toolchain on your system simply
run cmake with the path to the libwebm repo:
$ cmake path/to/libwebm
On Windows the above command will produce Visual Studio project files for the
newest Visual Studio detected on the system. On Mac OS X and Linux systems, the
above command will produce a makefile.
To control what types of projects are generated the -G parameter is added to
the cmake command line. This argument must be followed by the name of a
generator. Running cmake with the --help argument will list the available
generators for your system.
On Mac OS X you would run the following command to generate Xcode projects:
$ cmake path/to/libwebm -G Xcode
On a Windows box you would run the following command to generate Visual Studio
2013 projects:
$ cmake path/to/libwebm -G "Visual Studio 12"
To generate 64-bit Windows Visual Studio 2013 projects:
$ cmake path/to/libwebm "Visual Studio 12 Win64"
CMake Makefiles: Debugging and Optimization
Unlike Visual Studio and Xcode projects, the build configuration for make builds
is controlled when you run cmake. The following examples demonstrate various
build configurations.
Omitting the build type produces makefiles that use build flags containing
neither optimization nor debug flags:
$ cmake path/to/libwebm
A makefile using release (optimized) flags is produced like this:
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=release
A release build with debug info can be produced as well:
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=relwithdebinfo
And your standard debug build will be produced using:
$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=debug
Tests
To enable libwebm tests add -DENABLE_TESTS=ON CMake generation command line. For
example:
$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON
Libwebm tests depend on googletest. By default googletest is expected to be a
sibling directory of the Libwebm repository. To change that, update your CMake
command to be similar to the following:
$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON \
-DGTEST_SRC_DIR=/path/to/googletest
The tests rely upon the LIBWEBM_TEST_DATA_PATH environment variable to locate
test input. The following example demonstrates running the muxer tests from the
build directory:
$ LIBWEBM_TEST_DATA_PATH=path/to/libwebm/testing/testdata ./mkvmuxer_tests
Note: Libwebm Googletest integration was built with googletest from
https://github.com/google/googletest.git at git revision
ddb8012eb48bc203aa93dcc2b22c1db516302b29.
CMake Include-what-you-use integration
Include-what-you-use is an analysis tool that helps ensure libwebm includes the
C/C++ header files actually in use. To enable the integration support
ENABLE_IWYU must be turned on at cmake run time:
$ cmake path/to/libwebm -G "Unix Makefiles" -DENABLE_IWYU=ON
This adds the iwyu target to the build. To run include-what-you-use:
$ make iwyu
The following requirements must be met for ENABLE_IWYU to enable the iwyu
target:
1. include-what-you-use and iwyu_tool.py must be in your PATH.
2. A python interpreter must be on the system and available to CMake.
The values of the following variables are used to determine if the requirements
have been met. Values to the right of the equals sign are what a successful run
might look like:
iwyu_path=/path/to/iwyu_tool.py
iwyu_tool_path=/path/to/include-what-you-use
PYTHONINTERP_FOUND=TRUE
An empty PYTHONINTERP_FOUND, or iwyu_path/iwyu_tool_path suffixed with NOTFOUND
are failures.
For Include-what-you-use setup instructions, see:
https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/InstructionsForUsers.md
If, when building the iwyu target, compile errors reporting failures loading
standard include files occur, one solution can be found here:
https://github.com/include-what-you-use/include-what-you-use/issues/100
CMake cross compile
To cross compile libwebm for Windows using mingw-w64 run cmake with the
following arguments:
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
path/to/libwebm
Note1: As of this writing googletest will not build via mingw-w64 without
disabling pthreads.
googletest hash: d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0
To build with tests when using mingw-w64 use the following arguments when
running CMake:
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
-DENABLE_TESTS=ON -Dgtest_disable_pthreads=ON path/to/libwebm
Note2: i686-w64-mingw32 is the default compiler. This can be controlled using
the MINGW_PREFIX variable:
$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \
-DMINGW_PREFIX=x86_64-w64-mingw32 path/to/libwebm
Bug reports
Bug reports can be filed in the libwebm issue tracker:
https://issues.webmproject.org/.
For security reports, select 'Security report' from the Template dropdown.

View File

@ -0,0 +1,4 @@
# This file is used by git cl to get repository specific information.
GERRIT_HOST: True
CODE_REVIEW_SERVER: chromium-review.googlesource.com
GERRIT_SQUASH_UPLOADS: False

View File

@ -0,0 +1,193 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef COMMON_WEBMIDS_H_
#define COMMON_WEBMIDS_H_
namespace libwebm {
enum MkvId {
kMkvEBML = 0x1A45DFA3,
kMkvEBMLVersion = 0x4286,
kMkvEBMLReadVersion = 0x42F7,
kMkvEBMLMaxIDLength = 0x42F2,
kMkvEBMLMaxSizeLength = 0x42F3,
kMkvDocType = 0x4282,
kMkvDocTypeVersion = 0x4287,
kMkvDocTypeReadVersion = 0x4285,
kMkvVoid = 0xEC,
kMkvSignatureSlot = 0x1B538667,
kMkvSignatureAlgo = 0x7E8A,
kMkvSignatureHash = 0x7E9A,
kMkvSignaturePublicKey = 0x7EA5,
kMkvSignature = 0x7EB5,
kMkvSignatureElements = 0x7E5B,
kMkvSignatureElementList = 0x7E7B,
kMkvSignedElement = 0x6532,
// segment
kMkvSegment = 0x18538067,
// Meta Seek Information
kMkvSeekHead = 0x114D9B74,
kMkvSeek = 0x4DBB,
kMkvSeekID = 0x53AB,
kMkvSeekPosition = 0x53AC,
// Segment Information
kMkvInfo = 0x1549A966,
kMkvTimecodeScale = 0x2AD7B1,
kMkvDuration = 0x4489,
kMkvDateUTC = 0x4461,
kMkvTitle = 0x7BA9,
kMkvMuxingApp = 0x4D80,
kMkvWritingApp = 0x5741,
// Cluster
kMkvCluster = 0x1F43B675,
kMkvTimecode = 0xE7,
kMkvPrevSize = 0xAB,
kMkvBlockGroup = 0xA0,
kMkvBlock = 0xA1,
kMkvBlockDuration = 0x9B,
kMkvReferenceBlock = 0xFB,
kMkvLaceNumber = 0xCC,
kMkvSimpleBlock = 0xA3,
kMkvBlockAdditions = 0x75A1,
kMkvBlockMore = 0xA6,
kMkvBlockAddID = 0xEE,
kMkvBlockAdditional = 0xA5,
kMkvDiscardPadding = 0x75A2,
// Track
kMkvTracks = 0x1654AE6B,
kMkvTrackEntry = 0xAE,
kMkvTrackNumber = 0xD7,
kMkvTrackUID = 0x73C5,
kMkvTrackType = 0x83,
kMkvFlagEnabled = 0xB9,
kMkvFlagDefault = 0x88,
kMkvFlagForced = 0x55AA,
kMkvFlagLacing = 0x9C,
kMkvDefaultDuration = 0x23E383,
kMkvMaxBlockAdditionID = 0x55EE,
kMkvName = 0x536E,
kMkvLanguage = 0x22B59C,
kMkvCodecID = 0x86,
kMkvCodecPrivate = 0x63A2,
kMkvCodecName = 0x258688,
kMkvCodecDelay = 0x56AA,
kMkvSeekPreRoll = 0x56BB,
// video
kMkvVideo = 0xE0,
kMkvFlagInterlaced = 0x9A,
kMkvStereoMode = 0x53B8,
kMkvAlphaMode = 0x53C0,
kMkvPixelWidth = 0xB0,
kMkvPixelHeight = 0xBA,
kMkvPixelCropBottom = 0x54AA,
kMkvPixelCropTop = 0x54BB,
kMkvPixelCropLeft = 0x54CC,
kMkvPixelCropRight = 0x54DD,
kMkvDisplayWidth = 0x54B0,
kMkvDisplayHeight = 0x54BA,
kMkvDisplayUnit = 0x54B2,
kMkvAspectRatioType = 0x54B3,
kMkvColourSpace = 0x2EB524,
kMkvFrameRate = 0x2383E3,
// end video
// colour
kMkvColour = 0x55B0,
kMkvMatrixCoefficients = 0x55B1,
kMkvBitsPerChannel = 0x55B2,
kMkvChromaSubsamplingHorz = 0x55B3,
kMkvChromaSubsamplingVert = 0x55B4,
kMkvCbSubsamplingHorz = 0x55B5,
kMkvCbSubsamplingVert = 0x55B6,
kMkvChromaSitingHorz = 0x55B7,
kMkvChromaSitingVert = 0x55B8,
kMkvRange = 0x55B9,
kMkvTransferCharacteristics = 0x55BA,
kMkvPrimaries = 0x55BB,
kMkvMaxCLL = 0x55BC,
kMkvMaxFALL = 0x55BD,
// mastering metadata
kMkvMasteringMetadata = 0x55D0,
kMkvPrimaryRChromaticityX = 0x55D1,
kMkvPrimaryRChromaticityY = 0x55D2,
kMkvPrimaryGChromaticityX = 0x55D3,
kMkvPrimaryGChromaticityY = 0x55D4,
kMkvPrimaryBChromaticityX = 0x55D5,
kMkvPrimaryBChromaticityY = 0x55D6,
kMkvWhitePointChromaticityX = 0x55D7,
kMkvWhitePointChromaticityY = 0x55D8,
kMkvLuminanceMax = 0x55D9,
kMkvLuminanceMin = 0x55DA,
// end mastering metadata
// end colour
// projection
kMkvProjection = 0x7670,
kMkvProjectionType = 0x7671,
kMkvProjectionPrivate = 0x7672,
kMkvProjectionPoseYaw = 0x7673,
kMkvProjectionPosePitch = 0x7674,
kMkvProjectionPoseRoll = 0x7675,
// end projection
// audio
kMkvAudio = 0xE1,
kMkvSamplingFrequency = 0xB5,
kMkvOutputSamplingFrequency = 0x78B5,
kMkvChannels = 0x9F,
kMkvBitDepth = 0x6264,
// end audio
// ContentEncodings
kMkvContentEncodings = 0x6D80,
kMkvContentEncoding = 0x6240,
kMkvContentEncodingOrder = 0x5031,
kMkvContentEncodingScope = 0x5032,
kMkvContentEncodingType = 0x5033,
kMkvContentCompression = 0x5034,
kMkvContentCompAlgo = 0x4254,
kMkvContentCompSettings = 0x4255,
kMkvContentEncryption = 0x5035,
kMkvContentEncAlgo = 0x47E1,
kMkvContentEncKeyID = 0x47E2,
kMkvContentSignature = 0x47E3,
kMkvContentSigKeyID = 0x47E4,
kMkvContentSigAlgo = 0x47E5,
kMkvContentSigHashAlgo = 0x47E6,
kMkvContentEncAESSettings = 0x47E7,
kMkvAESSettingsCipherMode = 0x47E8,
kMkvAESSettingsCipherInitData = 0x47E9,
// end ContentEncodings
// Cueing Data
kMkvCues = 0x1C53BB6B,
kMkvCuePoint = 0xBB,
kMkvCueTime = 0xB3,
kMkvCueTrackPositions = 0xB7,
kMkvCueTrack = 0xF7,
kMkvCueClusterPosition = 0xF1,
kMkvCueBlockNumber = 0x5378,
// Chapters
kMkvChapters = 0x1043A770,
kMkvEditionEntry = 0x45B9,
kMkvChapterAtom = 0xB6,
kMkvChapterUID = 0x73C4,
kMkvChapterStringUID = 0x5654,
kMkvChapterTimeStart = 0x91,
kMkvChapterTimeEnd = 0x92,
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
kMkvChapCountry = 0x437E,
// Tags
kMkvTags = 0x1254C367,
kMkvTag = 0x7373,
kMkvSimpleTag = 0x67C8,
kMkvTagName = 0x45A3,
kMkvTagString = 0x4487
};
} // namespace libwebm
#endif // COMMON_WEBMIDS_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVMUXER_MKVMUXERTYPES_H_
#define MKVMUXER_MKVMUXERTYPES_H_
namespace mkvmuxer {
typedef unsigned char uint8;
typedef short int16;
typedef int int32;
typedef unsigned int uint32;
typedef long long int64;
typedef unsigned long long uint64;
} // namespace mkvmuxer
// Copied from Chromium basictypes.h
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define LIBWEBM_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // MKVMUXER_MKVMUXERTYPES_HPP_

View File

@ -0,0 +1,737 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "mkvmuxerutil.h"
#ifdef __ANDROID__
#include <fcntl.h>
#include <unistd.h>
#endif
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <new>
#include "../common/webmids.h"
#include "mkvmuxer.h"
#include "mkvwriter.h"
namespace mkvmuxer {
namespace {
// Date elements are always 8 octets in size.
const int kDateElementSize = 8;
uint64 WriteBlock(IMkvWriter* writer, const Frame* const frame, int64 timecode,
uint64 timecode_scale) {
uint64 block_additional_elem_size = 0;
uint64 block_addid_elem_size = 0;
uint64 block_more_payload_size = 0;
uint64 block_more_elem_size = 0;
uint64 block_additions_payload_size = 0;
uint64 block_additions_elem_size = 0;
if (frame->additional()) {
block_additional_elem_size =
EbmlElementSize(libwebm::kMkvBlockAdditional, frame->additional(),
frame->additional_length());
block_addid_elem_size = EbmlElementSize(
libwebm::kMkvBlockAddID, static_cast<uint64>(frame->add_id()));
block_more_payload_size =
block_addid_elem_size + block_additional_elem_size;
block_more_elem_size =
EbmlMasterElementSize(libwebm::kMkvBlockMore, block_more_payload_size) +
block_more_payload_size;
block_additions_payload_size = block_more_elem_size;
block_additions_elem_size =
EbmlMasterElementSize(libwebm::kMkvBlockAdditions,
block_additions_payload_size) +
block_additions_payload_size;
}
uint64 discard_padding_elem_size = 0;
if (frame->discard_padding() != 0) {
discard_padding_elem_size =
EbmlElementSize(libwebm::kMkvDiscardPadding,
static_cast<int64>(frame->discard_padding()));
}
const uint64 reference_block_timestamp =
frame->reference_block_timestamp() / timecode_scale;
uint64 reference_block_elem_size = 0;
if (!frame->is_key()) {
reference_block_elem_size =
EbmlElementSize(libwebm::kMkvReferenceBlock, reference_block_timestamp);
}
const uint64 duration = frame->duration() / timecode_scale;
uint64 block_duration_elem_size = 0;
if (duration > 0)
block_duration_elem_size =
EbmlElementSize(libwebm::kMkvBlockDuration, duration);
const uint64 block_payload_size = 4 + frame->length();
const uint64 block_elem_size =
EbmlMasterElementSize(libwebm::kMkvBlock, block_payload_size) +
block_payload_size;
const uint64 block_group_payload_size =
block_elem_size + block_additions_elem_size + block_duration_elem_size +
discard_padding_elem_size + reference_block_elem_size;
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockGroup,
block_group_payload_size)) {
return 0;
}
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlock, block_payload_size))
return 0;
if (WriteUInt(writer, frame->track_number()))
return 0;
if (SerializeInt(writer, timecode, 2))
return 0;
// For a Block, flags is always 0.
if (SerializeInt(writer, 0, 1))
return 0;
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
return 0;
if (frame->additional()) {
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockAdditions,
block_additions_payload_size)) {
return 0;
}
if (!WriteEbmlMasterElement(writer, libwebm::kMkvBlockMore,
block_more_payload_size))
return 0;
if (!WriteEbmlElement(writer, libwebm::kMkvBlockAddID,
static_cast<uint64>(frame->add_id())))
return 0;
if (!WriteEbmlElement(writer, libwebm::kMkvBlockAdditional,
frame->additional(), frame->additional_length())) {
return 0;
}
}
if (frame->discard_padding() != 0 &&
!WriteEbmlElement(writer, libwebm::kMkvDiscardPadding,
static_cast<int64>(frame->discard_padding()))) {
return false;
}
if (!frame->is_key() && !WriteEbmlElement(writer, libwebm::kMkvReferenceBlock,
reference_block_timestamp)) {
return false;
}
if (duration > 0 &&
!WriteEbmlElement(writer, libwebm::kMkvBlockDuration, duration)) {
return false;
}
return EbmlMasterElementSize(libwebm::kMkvBlockGroup,
block_group_payload_size) +
block_group_payload_size;
}
uint64 WriteSimpleBlock(IMkvWriter* writer, const Frame* const frame,
int64 timecode) {
if (WriteID(writer, libwebm::kMkvSimpleBlock))
return 0;
const int32 size = static_cast<int32>(frame->length()) + 4;
if (WriteUInt(writer, size))
return 0;
if (WriteUInt(writer, static_cast<uint64>(frame->track_number())))
return 0;
if (SerializeInt(writer, timecode, 2))
return 0;
uint64 flags = 0;
if (frame->is_key())
flags |= 0x80;
if (SerializeInt(writer, flags, 1))
return 0;
if (writer->Write(frame->frame(), static_cast<uint32>(frame->length())))
return 0;
return GetUIntSize(libwebm::kMkvSimpleBlock) + GetCodedUIntSize(size) + 4 +
frame->length();
}
} // namespace
int32 GetCodedUIntSize(uint64 value) {
if (value < 0x000000000000007FULL)
return 1;
else if (value < 0x0000000000003FFFULL)
return 2;
else if (value < 0x00000000001FFFFFULL)
return 3;
else if (value < 0x000000000FFFFFFFULL)
return 4;
else if (value < 0x00000007FFFFFFFFULL)
return 5;
else if (value < 0x000003FFFFFFFFFFULL)
return 6;
else if (value < 0x0001FFFFFFFFFFFFULL)
return 7;
return 8;
}
int32 GetUIntSize(uint64 value) {
if (value < 0x0000000000000100ULL)
return 1;
else if (value < 0x0000000000010000ULL)
return 2;
else if (value < 0x0000000001000000ULL)
return 3;
else if (value < 0x0000000100000000ULL)
return 4;
else if (value < 0x0000010000000000ULL)
return 5;
else if (value < 0x0001000000000000ULL)
return 6;
else if (value < 0x0100000000000000ULL)
return 7;
return 8;
}
int32 GetIntSize(int64 value) {
// Doubling the requested value ensures positive values with their high bit
// set are written with 0-padding to avoid flipping the signedness.
const uint64 v = (value < 0) ? value ^ -1LL : value;
return GetUIntSize(2 * v);
}
uint64 EbmlMasterElementSize(uint64 type, uint64 value) {
// Size of EBML ID
int32 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += GetCodedUIntSize(value);
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, int64 value) {
// Size of EBML ID
int32 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += GetIntSize(value);
// Size of Datasize
ebml_size++;
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, uint64 value) {
return EbmlElementSize(type, value, 0);
}
uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size) {
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += (fixed_size > 0) ? fixed_size : GetUIntSize(value);
// Size of Datasize
ebml_size++;
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, float /* value */) {
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += sizeof(float);
// Size of Datasize
ebml_size++;
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, const char* value) {
if (!value)
return 0;
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += strlen(value);
// Size of Datasize
ebml_size += GetCodedUIntSize(strlen(value));
return ebml_size;
}
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size) {
if (!value)
return 0;
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += size;
// Size of Datasize
ebml_size += GetCodedUIntSize(size);
return ebml_size;
}
uint64 EbmlDateElementSize(uint64 type) {
// Size of EBML ID
uint64 ebml_size = GetUIntSize(type);
// Datasize
ebml_size += kDateElementSize;
// Size of Datasize
ebml_size++;
return ebml_size;
}
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size) {
if (!writer || size < 1 || size > 8)
return -1;
for (int32 i = 1; i <= size; ++i) {
const int32 byte_count = size - i;
const int32 bit_count = byte_count * 8;
const int64 bb = value >> bit_count;
const uint8 b = static_cast<uint8>(bb);
const int32 status = writer->Write(&b, 1);
if (status < 0)
return status;
}
return 0;
}
int32 SerializeFloat(IMkvWriter* writer, float f) {
if (!writer)
return -1;
assert(sizeof(uint32) == sizeof(float));
// This union is merely used to avoid a reinterpret_cast from float& to
// uint32& which will result in violation of strict aliasing.
union U32 {
uint32 u32;
float f;
} value;
value.f = f;
for (int32 i = 1; i <= 4; ++i) {
const int32 byte_count = 4 - i;
const int32 bit_count = byte_count * 8;
const uint8 byte = static_cast<uint8>(value.u32 >> bit_count);
const int32 status = writer->Write(&byte, 1);
if (status < 0)
return status;
}
return 0;
}
int32 WriteUInt(IMkvWriter* writer, uint64 value) {
if (!writer)
return -1;
int32 size = GetCodedUIntSize(value);
return WriteUIntSize(writer, value, size);
}
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size) {
if (!writer || size < 0 || size > 8)
return -1;
if (size > 0) {
const uint64 bit = 1LL << (size * 7);
if (value > (bit - 2))
return -1;
value |= bit;
} else {
size = 1;
int64 bit;
for (;;) {
bit = 1LL << (size * 7);
const uint64 max = bit - 2;
if (value <= max)
break;
++size;
}
if (size > 8)
return false;
value |= bit;
}
return SerializeInt(writer, value, size);
}
int32 WriteID(IMkvWriter* writer, uint64 type) {
if (!writer)
return -1;
writer->ElementStartNotify(type, writer->Position());
const int32 size = GetUIntSize(type);
return SerializeInt(writer, type, size);
}
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 type, uint64 size) {
if (!writer)
return false;
if (WriteID(writer, type))
return false;
if (WriteUInt(writer, size))
return false;
return true;
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value) {
return WriteEbmlElement(writer, type, value, 0);
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value,
uint64 fixed_size) {
if (!writer)
return false;
if (WriteID(writer, type))
return false;
uint64 size = GetUIntSize(value);
if (fixed_size > 0) {
if (size > fixed_size)
return false;
size = fixed_size;
}
if (WriteUInt(writer, size))
return false;
if (SerializeInt(writer, value, static_cast<int32>(size)))
return false;
return true;
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value) {
if (!writer)
return false;
if (WriteID(writer, type))
return 0;
const uint64 size = GetIntSize(value);
if (WriteUInt(writer, size))
return false;
if (SerializeInt(writer, value, static_cast<int32>(size)))
return false;
return true;
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value) {
if (!writer)
return false;
if (WriteID(writer, type))
return false;
if (WriteUInt(writer, 4))
return false;
if (SerializeFloat(writer, value))
return false;
return true;
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value) {
if (!writer || !value)
return false;
if (WriteID(writer, type))
return false;
const uint64 length = strlen(value);
if (WriteUInt(writer, length))
return false;
if (writer->Write(value, static_cast<uint32>(length)))
return false;
return true;
}
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
uint64 size) {
if (!writer || !value || size < 1)
return false;
if (WriteID(writer, type))
return false;
if (WriteUInt(writer, size))
return false;
if (writer->Write(value, static_cast<uint32>(size)))
return false;
return true;
}
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value) {
if (!writer)
return false;
if (WriteID(writer, type))
return false;
if (WriteUInt(writer, kDateElementSize))
return false;
if (SerializeInt(writer, value, kDateElementSize))
return false;
return true;
}
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
Cluster* cluster) {
if (!writer || !frame || !frame->IsValid() || !cluster ||
!cluster->timecode_scale())
return 0;
// Technically the timecode for a block can be less than the
// timecode for the cluster itself (remember that block timecode
// is a signed, 16-bit integer). However, as a simplification we
// only permit non-negative cluster-relative timecodes for blocks.
const int64 relative_timecode = cluster->GetRelativeTimecode(
frame->timestamp() / cluster->timecode_scale());
if (relative_timecode < 0 || relative_timecode > kMaxBlockTimecode)
return 0;
return frame->CanBeSimpleBlock()
? WriteSimpleBlock(writer, frame, relative_timecode)
: WriteBlock(writer, frame, relative_timecode,
cluster->timecode_scale());
}
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size) {
if (!writer)
return false;
// Subtract one for the void ID and the coded size.
uint64 void_entry_size = size - 1 - GetCodedUIntSize(size - 1);
uint64 void_size = EbmlMasterElementSize(libwebm::kMkvVoid, void_entry_size) +
void_entry_size;
if (void_size != size)
return 0;
const int64 payload_position = writer->Position();
if (payload_position < 0)
return 0;
if (WriteID(writer, libwebm::kMkvVoid))
return 0;
if (WriteUInt(writer, void_entry_size))
return 0;
const uint8 value = 0;
for (int32 i = 0; i < static_cast<int32>(void_entry_size); ++i) {
if (writer->Write(&value, 1))
return 0;
}
const int64 stop_position = writer->Position();
if (stop_position < 0 ||
stop_position - payload_position != static_cast<int64>(void_size))
return 0;
return void_size;
}
void GetVersion(int32_t* major, int32_t* minor, int32_t* build, int32_t* revision) {
*major = 0;
*minor = 3;
*build = 3;
*revision = 0;
}
uint64 MakeUID(unsigned int* seed) {
uint64 uid = 0;
for (int i = 0; i < 7; ++i) { // avoid problems with 8-byte values
uid <<= 8;
// TODO(fgalligan): Move random number generation to platform specific code.
#ifdef _WIN32
(void)seed;
const int32 nn = rand();
#elif defined(__ANDROID__)
(void)seed;
int32 temp_num = 1;
int fd = open("/dev/urandom", O_RDONLY);
if (fd != -1) {
read(fd, &temp_num, sizeof(temp_num));
close(fd);
}
const int32 nn = temp_num;
#else
const int32 nn = rand_r(seed);
#endif
const int32 n = 0xFF & (nn >> 4); // throw away low-order bits
uid |= n;
}
return uid;
}
bool IsMatrixCoefficientsValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kGbr:
case mkvmuxer::Colour::kBt709:
case mkvmuxer::Colour::kUnspecifiedMc:
case mkvmuxer::Colour::kReserved:
case mkvmuxer::Colour::kFcc:
case mkvmuxer::Colour::kBt470bg:
case mkvmuxer::Colour::kSmpte170MMc:
case mkvmuxer::Colour::kSmpte240MMc:
case mkvmuxer::Colour::kYcocg:
case mkvmuxer::Colour::kBt2020NonConstantLuminance:
case mkvmuxer::Colour::kBt2020ConstantLuminance:
return true;
}
return false;
}
bool IsChromaSitingHorzValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kUnspecifiedCsh:
case mkvmuxer::Colour::kLeftCollocated:
case mkvmuxer::Colour::kHalfCsh:
return true;
}
return false;
}
bool IsChromaSitingVertValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kUnspecifiedCsv:
case mkvmuxer::Colour::kTopCollocated:
case mkvmuxer::Colour::kHalfCsv:
return true;
}
return false;
}
bool IsColourRangeValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kUnspecifiedCr:
case mkvmuxer::Colour::kBroadcastRange:
case mkvmuxer::Colour::kFullRange:
case mkvmuxer::Colour::kMcTcDefined:
return true;
}
return false;
}
bool IsTransferCharacteristicsValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kIturBt709Tc:
case mkvmuxer::Colour::kUnspecifiedTc:
case mkvmuxer::Colour::kReservedTc:
case mkvmuxer::Colour::kGamma22Curve:
case mkvmuxer::Colour::kGamma28Curve:
case mkvmuxer::Colour::kSmpte170MTc:
case mkvmuxer::Colour::kSmpte240MTc:
case mkvmuxer::Colour::kLinear:
case mkvmuxer::Colour::kLog:
case mkvmuxer::Colour::kLogSqrt:
case mkvmuxer::Colour::kIec6196624:
case mkvmuxer::Colour::kIturBt1361ExtendedColourGamut:
case mkvmuxer::Colour::kIec6196621:
case mkvmuxer::Colour::kIturBt202010bit:
case mkvmuxer::Colour::kIturBt202012bit:
case mkvmuxer::Colour::kSmpteSt2084:
case mkvmuxer::Colour::kSmpteSt4281Tc:
case mkvmuxer::Colour::kAribStdB67Hlg:
return true;
}
return false;
}
bool IsPrimariesValueValid(uint64_t value) {
switch (value) {
case mkvmuxer::Colour::kReservedP0:
case mkvmuxer::Colour::kIturBt709P:
case mkvmuxer::Colour::kUnspecifiedP:
case mkvmuxer::Colour::kReservedP3:
case mkvmuxer::Colour::kIturBt470M:
case mkvmuxer::Colour::kIturBt470Bg:
case mkvmuxer::Colour::kSmpte170MP:
case mkvmuxer::Colour::kSmpte240MP:
case mkvmuxer::Colour::kFilm:
case mkvmuxer::Colour::kIturBt2020:
case mkvmuxer::Colour::kSmpteSt4281P:
case mkvmuxer::Colour::kJedecP22Phosphors:
return true;
}
return false;
}
} // namespace mkvmuxer

View File

@ -0,0 +1,115 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVMUXER_MKVMUXERUTIL_H_
#define MKVMUXER_MKVMUXERUTIL_H_
#include <stdint.h>
#include "mkvmuxertypes.h"
namespace mkvmuxer {
class Cluster;
class Frame;
class IMkvWriter;
// TODO(tomfinegan): mkvmuxer:: integer types continue to be used here because
// changing them causes pain for downstream projects. It would be nice if a
// solution that allows removal of the mkvmuxer:: integer types while avoiding
// pain for downstream users of libwebm. Considering that mkvmuxerutil.{cc,h}
// are really, for the great majority of cases, EBML size calculation and writer
// functions, perhaps a more EBML focused utility would be the way to go as a
// first step.
const uint64 kEbmlUnknownValue = 0x01FFFFFFFFFFFFFFULL;
const int64 kMaxBlockTimecode = 0x07FFFLL;
// Writes out |value| in Big Endian order. Returns 0 on success.
int32 SerializeInt(IMkvWriter* writer, int64 value, int32 size);
// Writes out |f| in Big Endian order. Returns 0 on success.
int32 SerializeFloat(IMkvWriter* writer, float f);
// Returns the size in bytes of the element.
int32 GetUIntSize(uint64 value);
int32 GetIntSize(int64 value);
int32 GetCodedUIntSize(uint64 value);
uint64 EbmlMasterElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, int64 value);
uint64 EbmlElementSize(uint64 type, uint64 value);
uint64 EbmlElementSize(uint64 type, float value);
uint64 EbmlElementSize(uint64 type, const char* value);
uint64 EbmlElementSize(uint64 type, const uint8* value, uint64 size);
uint64 EbmlDateElementSize(uint64 type);
// Returns the size in bytes of the element assuming that the element was
// written using |fixed_size| bytes. If |fixed_size| is set to zero, then it
// computes the necessary number of bytes based on |value|.
uint64 EbmlElementSize(uint64 type, uint64 value, uint64 fixed_size);
// Creates an EBML coded number from |value| and writes it out. The size of
// the coded number is determined by the value of |value|. |value| must not
// be in a coded form. Returns 0 on success.
int32 WriteUInt(IMkvWriter* writer, uint64 value);
// Creates an EBML coded number from |value| and writes it out. The size of
// the coded number is determined by the value of |size|. |value| must not
// be in a coded form. Returns 0 on success.
int32 WriteUIntSize(IMkvWriter* writer, uint64 value, int32 size);
// Output an Mkv master element. Returns true if the element was written.
bool WriteEbmlMasterElement(IMkvWriter* writer, uint64 value, uint64 size);
// Outputs an Mkv ID, calls |IMkvWriter::ElementStartNotify|, and passes the
// ID to |SerializeInt|. Returns 0 on success.
int32 WriteID(IMkvWriter* writer, uint64 type);
// Output an Mkv non-master element. Returns true if the element was written.
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, int64 value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, float value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const char* value);
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, const uint8* value,
uint64 size);
bool WriteEbmlDateElement(IMkvWriter* writer, uint64 type, int64 value);
// Output an Mkv non-master element using fixed size. The element will be
// written out using exactly |fixed_size| bytes. If |fixed_size| is set to zero
// then it computes the necessary number of bytes based on |value|. Returns true
// if the element was written.
bool WriteEbmlElement(IMkvWriter* writer, uint64 type, uint64 value,
uint64 fixed_size);
// Output a Mkv Frame. It decides the correct element to write (Block vs
// SimpleBlock) based on the parameters of the Frame.
uint64 WriteFrame(IMkvWriter* writer, const Frame* const frame,
Cluster* cluster);
// Output a void element. |size| must be the entire size in bytes that will be
// void. The function will calculate the size of the void header and subtract
// it from |size|.
uint64 WriteVoidElement(IMkvWriter* writer, uint64 size);
// Returns the version number of the muxer in |major|, |minor|, |build|,
// and |revision|.
void GetVersion(int32_t* major, int32_t* minor, int32_t* build, int32_t* revision);
// Returns a random number to be used for UID, using |seed| to seed
// the random-number generator (see POSIX rand_r() for semantics).
uint64 MakeUID(unsigned int* seed);
// Colour field validation helpers. All return true when |value| is valid.
bool IsMatrixCoefficientsValueValid(uint64_t value);
bool IsChromaSitingHorzValueValid(uint64_t value);
bool IsChromaSitingVertValueValid(uint64_t value);
bool IsColourRangeValueValid(uint64_t value);
bool IsTransferCharacteristicsValueValid(uint64_t value);
bool IsPrimariesValueValid(uint64_t value);
} // namespace mkvmuxer
#endif // MKVMUXER_MKVMUXERUTIL_H_

View File

@ -0,0 +1,99 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "mkvwriter.h"
#include <sys/types.h>
#ifdef _MSC_VER
#include <share.h> // for _SH_DENYWR
#endif
namespace mkvmuxer {
MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}
MkvWriter::MkvWriter(File* fp) : file_(fp), writer_owns_file_(false), use_write_cb_(false) {}
MkvWriter::MkvWriter(void* userdata, int(*cb)(void* userdata, const void* buffer, uint32 length)) : write_cb_(cb), cb_userdata_(userdata), writer_owns_file_(false), use_write_cb_(true) {}
MkvWriter::~MkvWriter() { Close(); }
int32 MkvWriter::Write(const void* buffer, uint32 length) {
if (!file_ && !write_cb_)
return -1;
if (length == 0)
return 0;
if (buffer == NULL)
return -1;
size_t bytes_written = 0;
if(!use_write_cb_){
bytes_written = file_->write((const uint8_t *)buffer, (size_t)length);
} else {
bytes_written = write_cb_(cb_userdata_, buffer, length);
write_cb_written_ += bytes_written;
}
return (bytes_written == length) ? 0 : -1;
}
bool MkvWriter::Open(const char* filename) {
return false;
}
void MkvWriter::Close() {
if (file_ && writer_owns_file_) {
file_->close();
}
file_ = NULL;
}
int64 MkvWriter::Position() const {
if (!file_ || use_write_cb_)
return write_cb_written_;
return file_->position();
}
int32 MkvWriter::Position(int64 position) {
if (use_write_cb_ && write_cb_written_ == position)
return 0;
if (!file_ || use_write_cb_)
return -1;
return file_->seek(position);
// #ifdef _MSC_VER
// return _fseeki64(file_, position, SEEK_SET);
// #elif defined(_WIN32)
// return fseeko64(file_, static_cast<off_t>(position), SEEK_SET);
// #elif !(defined(__ANDROID__) && __ANDROID_API__ < 24 && !defined(__LP64__) && \
// defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
// // POSIX.1 has fseeko and ftello. fseeko and ftello are not available before
// // Android API level 24. See
// // https://android.googlesource.com/platform/bionic/+/main/docs/32-bit-abi.md
// // return fseeko(file_, static_cast<off_t>(position), SEEK_SET);
// #else
// return fseek(file_, static_cast<long>(position), SEEK_SET);
// #endif
// return -1;
}
bool MkvWriter::Seekable() const {
if (!file_ || use_write_cb_)
return false;
return true;
}
void MkvWriter::ElementStartNotify(uint64, int64) {}
} // namespace mkvmuxer

View File

@ -0,0 +1,58 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVMUXER_MKVWRITER_H_
#define MKVMUXER_MKVWRITER_H_
#include <stdio.h>
#include "Fs.h"
#include "mkvmuxer.h"
#include "mkvmuxertypes.h"
namespace mkvmuxer {
// Default implementation of the IMkvWriter interface on Windows.
class MkvWriter : public IMkvWriter {
public:
MkvWriter();
MkvWriter(File* fp);
MkvWriter(void* userdata, int(*cb)(void* userdata, const void* buffer, uint32 length));
virtual ~MkvWriter();
// IMkvWriter interface
virtual int64 Position() const;
virtual int32 Position(int64 position);
virtual bool Seekable() const;
virtual int32 Write(const void* buffer, uint32 length);
virtual void ElementStartNotify(uint64 element_id, int64 position);
// Creates and opens a file for writing. |filename| is the name of the file
// to open. This function will overwrite the contents of |filename|. Returns
// true on success.
bool Open(const char* filename);
// Closes an opened file.
void Close();
private:
// File handle to output file.
File* file_;
// Callback function to output stream.
int(*write_cb_)(void* userdata, const void* buffer, uint32 length);
void* cb_userdata_;
bool writer_owns_file_;
bool use_write_cb_;
uint32_t write_cb_written_ = 0;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(MkvWriter);
};
} // namespace mkvmuxer
#endif // MKVMUXER_MKVWRITER_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,12 @@ enum : uint32_t {
OPUS_DECODER = 2,
};
// I2S encoder type
enum : uint8_t {
MP3_ENCODER = 1,
OPUS_ENCODER = 2,
};
#define I2S_SLOTS 2
#define AUDIO_SETTINGS_VERSION 2

View File

@ -93,19 +93,20 @@ struct AUDIO_I2S_MP3_t {
#endif // defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
char mic_path[32];
uint8_t mic_stop;
int8_t mic_error;
bool mic_stop = false;
bool use_stream = false;
bool task_running = false;
bool task_has_ended = false;
bool task_loop_mode = false;
// SHINE
// RECORD/STREAM/ENCODING
uint32_t recdur;
uint8_t stream_active;
uint8_t stream_enable;
uint8_t encoder_type;
bool stream_active;
bool stream_enable;
WiFiClient client;
ESP8266WebServer *MP3Server;
ESP8266WebServer *StreamServer;
// I2S_BRIDGE
BRIDGE_MODE bridge_mode;
@ -344,182 +345,6 @@ void CmndI2SConfig(void) {
cfg->rx.dma_desc_num
);
}
/*********************************************************************************************\
* microphone related functions
\*********************************************************************************************/
// micro to mp3 file or stream
void I2sMicTask(void *arg){
int8_t error = 0;
uint8_t *ucp;
int written;
shine_config_t config;
shine_t s = nullptr;
uint16_t samples_per_pass;
File mp3_out = (File)nullptr;
int16_t *buffer = nullptr;
uint16_t bytesize;
uint16_t bwritten;
uint32_t ctime;
uint32_t gain = audio_i2s.Settings->rx.gain;
uint32_t timeForOneRead;
if (!audio_i2s_mp3.use_stream) {
mp3_out = ufsp->open(audio_i2s_mp3.mic_path, "w");
if (!mp3_out) {
error = 1;
goto exit;
}
} else {
if (!audio_i2s_mp3.stream_active) {
error = 2;
audio_i2s_mp3.use_stream = 0;
goto exit;
}
audio_i2s_mp3.client.flush();
audio_i2s_mp3.client.setTimeout(3);
audio_i2s_mp3.client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: audio/mpeg;\r\n\r\n");
// Webserver->send(200, "application/octet-stream", "");
//"Content-Type: audio/mp3;\r\n\r\n");
}
shine_set_config_mpeg_defaults(&config.mpeg);
if (audio_i2s.Settings->rx.channels == 1) {
config.mpeg.mode = MONO;
} else {
config.mpeg.mode = STEREO;
}
// config.mpeg.bitr = 128; - this is default anyway, but maybe we want to make it a variable in the future
config.wave.samplerate = audio_i2s.Settings->rx.sample_rate;
config.wave.channels = (channels)(audio_i2s.Settings->rx.channels);
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
error = 3;
goto exit;
}
s = shine_initialise(&config);
if (!s) {
error = 4;
goto exit;
}
samples_per_pass = shine_samples_per_pass(s);
bytesize = samples_per_pass * 2 * (audio_i2s.Settings->rx.channels);
buffer = (int16_t*)malloc(bytesize);
if (!buffer) {
error = 5;
goto exit;
}
ctime = TasmotaGlobal.uptime;
timeForOneRead = 1000 / ((audio_i2s.Settings->rx.sample_rate / (samples_per_pass * audio_i2s.Settings->rx.channels )));
timeForOneRead -= 1; // be very in time
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: samples %u, bytesize %u, time: %u"),samples_per_pass, bytesize, timeForOneRead);
while (!audio_i2s_mp3.mic_stop) {
TickType_t xLastWakeTime = xTaskGetTickCount();
size_t bytes_read;
// bytes_read = audio_i2s.in->readMic((uint8_t*)buffer, bytesize, true /*dc_block*/, false /*apply_gain*/, true /*lowpass*/, nullptr /*peak_ptr*/);
i2s_channel_read(audio_i2s.in->getRxHandle(), (void*)buffer, bytesize, &bytes_read, pdMS_TO_TICKS(3));
if(bytes_read < bytesize) AddLog(LOG_LEVEL_DEBUG, PSTR("!! %u, %u"), bytes_read, bytesize);
if (gain > 1) {
// set gain the "old way"
int16_t _gain = gain / 16;
for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) {
buffer[cnt] *= _gain;
}
}
ucp = shine_encode_buffer_interleaved(s, buffer, &written);
if (!audio_i2s_mp3.use_stream) {
bwritten = mp3_out.write(ucp, written);
if (bwritten != written) {
break;
}
} else {
audio_i2s_mp3.client.write((const char*)ucp, written);
if (!audio_i2s_mp3.client.connected()) {
break;
}
}
audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead));
}
ucp = shine_flush(s, &written);
if (!audio_i2s_mp3.use_stream) {
mp3_out.write(ucp, written);
} else {
audio_i2s_mp3.client.write((const char*)ucp, written);
}
exit:
if (s) {
shine_close(s);
}
if (mp3_out) {
mp3_out.close();
AddLog(LOG_LEVEL_INFO, PSTR("I2S: MP3 file closed"));
}
if (buffer) {
free(buffer);
}
if (audio_i2s_mp3.use_stream) {
audio_i2s_mp3.client.stop();
}
audio_i2s.in->stopRx();
audio_i2s_mp3.mic_stop = 0;
audio_i2s_mp3.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("I2S: mp3task result code: %d"), error);
audio_i2s_mp3.mic_task_handle = 0;
audio_i2s_mp3.recdur = 0;
audio_i2s_mp3.stream_active = 0;
vTaskDelete(NULL);
}
int32_t I2sRecordShine(char *path) {
esp_err_t err = ESP_OK;
switch(audio_i2s.Settings->rx.sample_rate){
case 32000: case 48000: case 44100:
break; // supported
default:
AddLog(LOG_LEVEL_INFO, PSTR("I2S: unsupported sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate);
return -1;
}
AddLog(LOG_LEVEL_INFO, PSTR("I2S: accepted sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate);
#ifdef USE_I2S_MP3
if (audio_i2s_mp3.decoder) return 0;
#endif
strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path));
audio_i2s_mp3.mic_stop = 0;
uint32_t stack = 8000;
audio_i2s_mp3.use_stream = !strcmp(audio_i2s_mp3.mic_path, "stream.mp3");
audio_i2s.in->startRx();
err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1);
return err;
}
/*********************************************************************************************\
* Driver Settings load and save using filesystem
@ -1114,42 +939,31 @@ void CmndI2SMicRec(void) {
ResponseCmndChar("I2S Mic not configured");
return;
}
if (audio_i2s_mp3.preallocateCodec == nullptr){
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: try late codec buffer allocation"));
audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize);
}
if (audio_i2s_mp3.preallocateCodec != nullptr) {
if (XdrvMailbox.data_len > 0) {
if (!strncmp(XdrvMailbox.data, "-?", 2)) {
Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur);
} else {
int err = I2sRecordShine(XdrvMailbox.data);
if(err == pdPASS){
ResponseCmndChar(XdrvMailbox.data);
} else {
ResponseCmndChar_P(PSTR("Did not launch recording task"));
}
}
if (XdrvMailbox.data_len > 0) {
if (!strncmp(XdrvMailbox.data, "-?", 2)) {
Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur);
} else {
if (audio_i2s_mp3.mic_task_handle) {
// stop task
audio_i2s_mp3.mic_stop = 1;
while (audio_i2s_mp3.mic_stop) {
delay(1);
}
ResponseCmndChar_P(PSTR("Stopped"));
}
else {
ResponseCmndChar_P(PSTR("No running recording"));
audio_i2s_mp3.use_stream = false;
int err = I2sRecord(XdrvMailbox.data, XdrvMailbox.index);
// int err = I2sRecordShine(XdrvMailbox.data);
if(err == pdPASS){
ResponseCmndChar(XdrvMailbox.data);
} else {
ResponseCmndChar_P(PSTR("Did not launch recording task"));
}
}
}
else{
if (audio_i2s.in){
ResponseCmndChar_P(PSTR("need PSRAM for MP3 recording"));
} else {
if (audio_i2s_mp3.mic_task_handle) {
// stop task
audio_i2s_mp3.mic_stop = 1;
while (audio_i2s_mp3.mic_stop) {
delay(1);
}
ResponseCmndChar_P(PSTR("Stopped"));
}
else{
ResponseCmndChar_P(PSTR("no mic configured"));
else {
ResponseCmndChar_P(PSTR("No running recording"));
}
}
}
@ -1158,7 +972,7 @@ void CmndI2SMicRec(void) {
* Interface
\*********************************************************************************************/
void I2sMp3Loop(void);
void I2sStreamLoop(void);
void I2sMp3Init(uint32_t on);
void MP3ShowStream(void);
@ -1177,17 +991,12 @@ bool Xdrv42(uint32_t function) {
break;
case FUNC_LOOP:
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
I2sMp3Loop();
I2sStreamLoop();
#endif
#if defined(I2S_BRIDGE)
i2s_bridge_loop();
#endif
break;
case FUNC_WEB_ADD_HANDLER:
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
// audio_i2s.Settings->tx.stream_enable = 1;
// I2sMp3Init(1);
#endif
#if defined(I2S_BRIDGE)
I2SBridgeInit();
#endif

View File

@ -1,5 +1,5 @@
/*
xdrv_42_i2s_audio.ino - Audio dac support for Tasmota
xdrv_42_i2s_mp3mic.ino - Audio dac support for Tasmota
Copyright (C) 2021 Gerhard Mutz and Theo Arends
@ -21,219 +21,397 @@
#if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5
#ifdef USE_I2S_AUDIO
// uint32_t SpeakerMic(uint8_t spkr) {
// esp_err_t err = ESP_OK;
class AudioEncoder
{
public:
AudioEncoder() {
inBuffer = nullptr;
outFrame = nullptr;
samplesPerPass = 0;
byteSize = 0;
};
virtual ~AudioEncoder() {};
virtual uint32_t begin(uint32_t samplingRate, uint32_t inputChannels) { return 0; };
virtual int encode(size_t samples) { return 0; };
virtual size_t stop() { return 0; };
virtual void setCB(void *cb){};
// // audio_i2s.mode = spkr;
// return err;
// }
public:
int16_t *inBuffer;
uint8_t *outFrame;
uint32_t samplesPerPass;
uint32_t byteSize;
File *file;
WiFiClient *client;
};
// #ifdef USE_SHINE
#ifdef MP3_MIC_STREAM
class AudioEncoderShineMP3 : public AudioEncoder
{
public:
AudioEncoderShineMP3(File *rec_file, WiFiClient *wifi) {
file = rec_file;
client = wifi;
};
virtual ~AudioEncoderShineMP3() {
if (s) {shine_close(s);}
if(inBuffer){free(inBuffer); }
if(file) {file->close();}
if(client) {client->stop();}
};
virtual uint32_t begin(uint32_t samplingRate, uint32_t inputChannels) {
shine_set_config_mpeg_defaults(&config.mpeg);
if (inputChannels == 1) {
config.mpeg.mode = MONO;
} else {
config.mpeg.mode = STEREO;
}
config.wave.samplerate = samplingRate;
config.wave.channels = (channels)inputChannels;
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {return 3;}
s = shine_initialise(&config);
if (!s) {return 4; }
// #include <layer3.h>
// #include <types.h>
samplesPerPass = shine_samples_per_pass(s);
byteSize = samplesPerPass * 2 * inputChannels;
// // micro to mp3 file or stream
// void mic_task(void *arg){
// int8_t error = 0;
// uint8_t *ucp;
// int written;
// shine_config_t config;
// shine_t s = nullptr;
// uint16_t samples_per_pass;
// File mp3_out = (File)nullptr;
// int16_t *buffer = nullptr;
// uint16_t bytesize;
// uint16_t bwritten;
// uint32_t ctime;
inBuffer = (int16_t*)malloc(byteSize); // byteSize = samplesPerPass * sizeof(int16_t)
if (!inBuffer) {return 5; }
return 0;
};
// if (!audio_i2s_mp3.use_stream) {
// mp3_out = ufsp->open(audio_i2s_mp3.mic_path, "w");
// if (!mp3_out) {
// error = 1;
// goto exit;
// }
// } else {
// if (!audio_i2s_mp3.stream_active) {
// error = 2;
// audio_i2s_mp3.use_stream = 0;
// goto exit;
// }
// audio_i2s_mp3.client.flush();
// audio_i2s_mp3.client.setTimeout(3);
// audio_i2s_mp3.client.print("HTTP/1.1 200 OK\r\n"
// "Content-Type: audio/mpeg;\r\n\r\n");
virtual size_t stop() {
int written;
outFrame = shine_flush(s, &written);
return written;
}
// // Webserver->send(200, "application/octet-stream", "");
// //"Content-Type: audio/mp3;\r\n\r\n");
// }
virtual int encode(size_t samples) {
int written;
outFrame = shine_encode_buffer_interleaved(s, inBuffer, &written);
return write(written);
};
// shine_set_config_mpeg_defaults(&config.mpeg);
protected:
int write(int len){
size_t written = 0;
if(file != nullptr){
written = file->write(outFrame,len);
} else if (client != nullptr){
if (client->connected()) {
written = client->write(outFrame, len);
}
}
return (written != (size_t)len);
}
shine_config_t config;
shine_t s;
};
#endif //MP3_MIC_STREAM
// if (audio_i2s.Settings->rx.channels == 1) {
// config.mpeg.mode = MONO;
// } else {
// config.mpeg.mode = STEREO;
// }
// config.mpeg.bitr = 128;
// config.wave.samplerate = audio_i2s.Settings->rx.sample_rate;
// config.wave.channels = (channels)audio_i2s.Settings->rx.channels;
#ifdef USE_I2S_OPUS
// if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
// error = 3;
// goto exit;
// }
#include "libwebm/mkvmuxer/mkvwriter.h"
class AudioEncoderOpusWebm;
typedef int (AudioEncoderOpusWebm::*packet_cb)(void *userdata, const void *buffer, size_t length);
class AudioEncoderOpusWebm : public AudioEncoder
{
public:
AudioEncoderOpusWebm(File *rec_file, WiFiClient *wifi) {
file = rec_file;
client = wifi;
};
// s = shine_initialise(&config);
// if (!s) {
// error = 4;
// goto exit;
// }
virtual ~AudioEncoderOpusWebm() {
if(inBuffer){ free(inBuffer); }
if(outFrame){ free(outFrame); }
opus_encoder_destroy(encoder);
if(file) {file->close();}
if(client) {client->stop();}
if(wifiBuffer){ free(wifiBuffer); }
delete muxer;
};
// samples_per_pass = shine_samples_per_pass(s);
// bytesize = samples_per_pass * 2 * audio_i2s.Settings->rx.channels;
virtual uint32_t begin(uint32_t samplingRate, uint32_t inputChannels) {
int error;
encoder = opus_encoder_create(samplingRate, inputChannels, samplingRate > 16000 ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error);
if (error != 0) { return error; }
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(complexity));
int _preskip;
opus_encoder_ctl(encoder, OPUS_GET_LOOKAHEAD((opus_int32*)&_preskip));
opusHeader.samplingRate = samplingRate;
opusHeader.inputChannels = inputChannels;
opusHeader.preSkip = _preskip;
// buffer = (int16_t*)malloc(bytesize);
// if (!buffer) {
// error = 5;
// goto exit;
// }
constexpr uint32_t frameDuration = 20; //ms
samplesPerPass = samplingRate/(1000/frameDuration);
byteSize = samplesPerPass * 2 * inputChannels;
inBuffer = (int16_t*)malloc(byteSize);
if (!inBuffer) {return 5; }
outFrame = (uint8_t*)malloc(maxBytes);
if (!outFrame) {return 5; }
if(client){
wifiBuffer = (uint8_t*)malloc(wifiBufferSize);
if (!wifiBuffer) {return 5; }
}
// ctime = TasmotaGlobal.uptime;
if(file){
muxer = new mkvmuxer::MkvWriter(file);
} else if (client){
muxer = new mkvmuxer::MkvWriter(this, &AudioEncoderOpusWebm::packet_cb);
}
timeCode = 0;
if (!muxer) {return 6; }
if (!muxerSegment.Init(muxer)) {return 7; }
mkvmuxer::SegmentInfo* info = muxerSegment.GetSegmentInfo();
if (info == nullptr) {return 8;}
info->set_writing_app("Tasmota");
// Add an audio track.
trackNumber = muxerSegment.AddAudioTrack(samplingRate, inputChannels, 0);
if (trackNumber == 0) {return 9; }
mkvmuxer::AudioTrack* audio = static_cast<mkvmuxer::AudioTrack*>(muxerSegment.GetTrackByNumber(trackNumber));
if (audio == nullptr) {return 10;}
audio->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
audio->set_bit_depth(16);
audio->set_codec_delay(_preskip * 1000000 / samplingRate); // Delay built into the code during decoding in nanoseconds.
audio->set_seek_pre_roll(80000000); // Amount of audio to discard after a seek - https://wiki.xiph.org/MatroskaOpus
if (!audio->SetCodecPrivate( (const uint8_t*)&opusHeader, sizeof(opusHeader))) {return 11;}
return 0;
}
// while (!audio_i2s_mp3.mic_stop) {
// uint32_t bytes_read;
// bytes_read = audio_i2s.in->readMic((uint8_t*)buffer, bytesize, true /*dc_block*/, false /*apply_gain*/, true /*lowpass*/, nullptr /*peak_ptr*/);
// // i2s_read(audio_i2s.mic_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_PERIOD_MS));
virtual int encode(size_t samples) {
opus_int32 len = opus_encode(encoder, inBuffer, samplesPerPass, outFrame, maxBytes);
if (len < 0) { return -1; }
if (len > 2) // ignore packets shorter than or equal to 2 bytes.
{
if(!muxerSegment.AddFrame(outFrame, len, trackNumber, timeCode, true)){
return -1;
}
}
timeCode += 20000 * 1000; // 20 ms in nanoseconds
return 0;
}
// if (audio_i2s.Settings->rx.gain > 1) {
// // set gain
// for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) {
// buffer[cnt] *= audio_i2s.Settings->rx.gain;
// }
// }
// ucp = shine_encode_buffer_interleaved(s, buffer, &written);
virtual size_t stop() {
bool success = muxerSegment.Finalize();
muxer->Close();
return success;
}
// if (!audio_i2s.Settings->tx.stream_enable) {
// bwritten = mp3_out.write(ucp, written);
// if (bwritten != written) {
// break;
// }
// } else {
// audio_i2s_mp3.client.write((const char*)ucp, written);
protected:
OpusEncoder *encoder;
const uint32_t maxBytes = 1296;
const uint32_t complexity = 1; // 1-10 - low to high quality
mkvmuxer::MkvWriter *muxer;
mkvmuxer::Segment muxerSegment;
// if (!audio_i2s_mp3.client.connected()) {
// break;
// }
// }
// audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
// }
uint64_t trackNumber = 0;
uint64_t timeCode = 0;
// ucp = shine_flush(s, &written);
struct __attribute__((packed)) {
char signatureHead[8] = {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'};
uint8_t version = 1;
uint8_t inputChannels = 1;
uint16_t preSkip = 3840;
uint32_t samplingRate = 0;
int16_t outputGain = 0;
uint8_t channelMappingFamily = 0;
} opusHeader;
// if (!audio_i2s_mp3.use_stream) {
// mp3_out.write(ucp, written);
// } else {
// audio_i2s_mp3.client.write((const char*)ucp, written);
// }
static constexpr size_t wifiBufferSize = CONFIG_LWIP_TCP_MSS; // should be <= TCP Maximum Segment Size
uint8_t *wifiBuffer;
size_t wifiPtr = 0;
static int packet_cb(void *userdata, const void *buffer, size_t length){
AudioEncoderOpusWebm *_enc = (AudioEncoderOpusWebm *)userdata;
int result = length;
if(_enc->wifiPtr + length > wifiBufferSize){
size_t written;
if (_enc->client->connected()) {
written = _enc->client->write(_enc->wifiBuffer, _enc->wifiPtr);
}
result = written == _enc->wifiPtr ? length : -1; //TODO - maybe tolerate some send errors
// AddLog(LOG_LEVEL_DEBUG, PSTR(">> %u, %u, %u"),written, _enc->wifiPtr, result);
_enc->wifiPtr = 0;
}
memcpy(_enc->wifiBuffer + _enc->wifiPtr,(uint8_t*)buffer,length);
_enc->wifiPtr += length;
return result;
}
};
#endif // USE_I2S_OPUS
// exit:
// if (s) {
// shine_close(s);
// }
// if (mp3_out) {
// mp3_out.close();
// }
// if (buffer) {
// free(buffer);
// }
// micro to mp3/webm - file or stream
void I2sMicTask(void *arg){
int8_t error = 0;
int written;
// if (audio_i2s_mp3.use_stream) {
// audio_i2s_mp3.client.stop();
// }
AudioEncoder *mic_enc;
File rec_file;
File *rec_file_ptr = nullptr;
// SpeakerMic(I2S_AUDIO_MODE_SPK);
// audio_i2s_mp3.mic_stop = 0;
// audio_i2s_mp3.mic_error = error;
// AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
// audio_i2s_mp3.mic_task_handle = 0;
// audio_i2s_mp3.recdur = 0;
// audio_i2s_mp3.stream_active = 0;
// vTaskDelete(NULL);
uint16_t bwritten;
uint32_t ctime;
uint32_t gain = audio_i2s.Settings->rx.gain;
uint32_t timeForOneRead;
uint32_t __enctime;
TickType_t xLastWakeTime;
// }
if (!audio_i2s_mp3.use_stream) {
rec_file = ufsp->open(audio_i2s_mp3.mic_path, "w");
if (!rec_file) {
error = 1;
goto exit;
} else {
rec_file_ptr = &rec_file;
}
} else {
if (!audio_i2s_mp3.stream_active) {
error = 2;
goto exit;
}
}
// int32_t i2s_record_shine(char *path) {
// esp_err_t err = ESP_OK;
if(audio_i2s_mp3.encoder_type == MP3_ENCODER){
#ifdef MP3_MIC_STREAM
mic_enc = new AudioEncoderShineMP3(rec_file_ptr, &audio_i2s_mp3.client);
#endif // MP3_MIC_STREAM
} else {
#ifdef USE_I2S_OPUS
mic_enc = new AudioEncoderOpusWebm(rec_file_ptr, &audio_i2s_mp3.client);
#endif // USE_I2S_OPUS
}
// if (audio_i2s.in) {
// if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return 0;
// }
if (audio_i2s_mp3.use_stream) {
audio_i2s_mp3.client.flush();
audio_i2s_mp3.client.setTimeout(3);
if(audio_i2s_mp3.encoder_type == MP3_ENCODER){
audio_i2s_mp3.client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: audio/mpeg;\r\n\r\n");
} else if (audio_i2s_mp3.encoder_type == OPUS_ENCODER){
audio_i2s_mp3.client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: audio/webm; codecs=opus\r\n\r\n");
}
}
// err = SpeakerMic(I2S_AUDIO_MODE_MIC);
// if (err) {
// if (audio_i2s.in) {
// SpeakerMic(I2S_AUDIO_MODE_SPK);
// }
// AddLog(LOG_LEVEL_INFO, PSTR("mic init error: %d"), err);
// return err;
// }
error = mic_enc->begin(audio_i2s.Settings->rx.sample_rate, audio_i2s.Settings->rx.channels);
if(error != 0){
goto exit;
}
// strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path));
ctime = TasmotaGlobal.uptime;
timeForOneRead = 1000 / ((audio_i2s.Settings->rx.sample_rate / (mic_enc->samplesPerPass * audio_i2s.Settings->rx.channels )));
// timeForOneRead -= 1; // be very in time
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: samples %u, bytesize %u, time: %u"),mic_enc->samplesPerPass, mic_enc->byteSize, timeForOneRead);
xLastWakeTime = xTaskGetTickCount();
// audio_i2s_mp3.mic_stop = 0;
while (!audio_i2s_mp3.mic_stop) {
size_t bytes_read;
// bytes_read = audio_i2s.in->readMic((uint8_t*)mic_enc->inBuffer, mic_enc->byteSize, true /*dc_block*/, false /*apply_gain*/, true /*lowpass*/, nullptr /*peak_ptr*/);
i2s_channel_read(audio_i2s.in->getRxHandle(), (void*)mic_enc->inBuffer, mic_enc->byteSize, &bytes_read, pdMS_TO_TICKS(1));
// uint32_t stack = 4096;
if (gain > 1) {
// set gain the "old way"
int16_t _gain = gain / 16;
for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) {
mic_enc->inBuffer[cnt] *= _gain;
}
}
// audio_i2s_mp3.use_stream = !strcmp(audio_i2s_mp3.mic_path, "stream.mp3");
__enctime = millis();
if(bytes_read != 0){
written = mic_enc->encode(bytes_read >> 1); //transmit samples, written is an error code
}
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("_mic: %u , %i, %i"), millis() - __enctime, written, bytes_read);
if(written < 0){
break;
}
// if (audio_i2s_mp3.use_stream) {
// stack = 8000;
// }
if (audio_i2s_mp3.use_stream) {
if (!audio_i2s_mp3.client.connected()) {
break;
}
}
// err = xTaskCreatePinnedToCore(mic_task, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1);
audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead));
}
// return err;
// }
written = mic_enc->stop();
// void Cmd_MicRec(void) {
if (!audio_i2s_mp3.use_stream) {
rec_file.write(mic_enc->outFrame, written);
} else {
audio_i2s_mp3.client.write((const char*)mic_enc->outFrame, written);
}
// if (XdrvMailbox.data_len > 0) {
// if (!strncmp(XdrvMailbox.data, "-?", 2)) {
// Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur);
// } else {
// i2s_record_shine(XdrvMailbox.data);
// ResponseCmndChar(XdrvMailbox.data);
// }
// } else {
// if (audio_i2s_mp3.mic_task_handle) {
// // stop task
// audio_i2s_mp3.mic_stop = 1;
// while (audio_i2s_mp3.mic_stop) {
// delay(1);
// }
// ResponseCmndChar_P(PSTR("Stopped"));
// }
// }
exit:
delete mic_enc;
audio_i2s_mp3.use_stream = false;
audio_i2s.in->stopRx();
audio_i2s_mp3.mic_stop = 0;
audio_i2s_mp3.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("I2S: record task result code: %d, min bytes stack free: %u"), error, (uint32_t)uxTaskGetStackHighWaterMark(NULL)*4);
audio_i2s_mp3.mic_task_handle = 0;
audio_i2s_mp3.recdur = 0;
audio_i2s_mp3.stream_active = false;
vTaskDelete(NULL);
}
// }
// #endif // USE_SHINE
int32_t I2sRecord(char *path, uint32_t encoder_type) {
esp_err_t err = ESP_OK;
uint32_t stack = 8000;
#ifdef USE_I2S_MP3
if (audio_i2s_mp3.decoder) return 0;
#endif
// // mic gain in factor not percent
// void Cmd_MicGain(void) {
// if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 256)) {
// if (audio_i2s.in) {
// audio_i2s.in->setRxGain(XdrvMailbox.payload);
// }
// if (audio_i2s.Settings) {
// audio_i2s.Settings->rx.gain = XdrvMailbox.payload * 16;
// }
// I2SSettingsSave(AUDIO_CONFIG_FILENAME);
// }
// ResponseCmndNumber(audio_i2s.Settings->rx.gain / 16);
// }
switch(encoder_type){
#ifdef MP3_MIC_STREAM
case MP3_ENCODER:
switch(audio_i2s.Settings->rx.sample_rate){
case 32000: case 48000: case 44100:
break; // supported
default:
AddLog(LOG_LEVEL_ERROR, PSTR("I2S: unsupported sample rate for MP3 encoding: %d Hz"), audio_i2s.Settings->rx.sample_rate);
return -1;
}
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: start MP3 encoding: %d Hz"), audio_i2s.Settings->rx.sample_rate);
break;
#endif // MP3_MIC_STREAM
#ifdef USE_I2S_OPUS
case OPUS_ENCODER:
switch(audio_i2s.Settings->rx.sample_rate){
case 48000: case 24000: case 16000: case 12000: case 8000:
stack = audio_i2s.Settings->rx.sample_rate/2 + 30000; //not the exact value, but okay'ish
break;
default:
AddLog(LOG_LEVEL_ERROR, PSTR("I2S: unsupported sample rate for OPUS encoding: %d Hz"), audio_i2s.Settings->rx.sample_rate);
return -1;
}
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: start OPUS encoding: %d Hz"), audio_i2s.Settings->rx.sample_rate);
break;
#endif // USE_I2S_OPUS
default:
AddLog(LOG_LEVEL_ERROR, PSTR("I2S: unsupported encoder"));
}
audio_i2s_mp3.encoder_type = encoder_type;
if (audio_i2s_mp3.preallocateCodec != nullptr){
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: free codec buffer"));
free(audio_i2s_mp3.preallocateCodec);
audio_i2s_mp3.preallocateCodec = nullptr;
}
if(!audio_i2s_mp3.use_stream){
strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path));
}
audio_i2s_mp3.mic_stop = 0;
audio_i2s.in->startRx();
err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1);
return err;
}
#endif // USE_I2S_AUDIO
#endif // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5

View File

@ -25,42 +25,57 @@
#endif
void Stream_mp3(void) {
void I2SMP3StreamInit(){
audio_i2s_mp3.encoder_type = MP3_ENCODER;
I2SstreamInit ();
}
void I2SOpusStreamInit(){
audio_i2s_mp3.encoder_type = OPUS_ENCODER;
I2SstreamInit ();
}
void I2SstreamInit (void) {
if (audio_i2s_mp3.stream_active) {
AddLog(LOG_LEVEL_INFO, PSTR("I2S: can not handle client - other MP3 task active"));
AddLog(LOG_LEVEL_INFO, PSTR("I2S: can not handle client - other stream task active"));
return;
}
AddLog(LOG_LEVEL_INFO, PSTR("I2S: Handle mp3server"));
audio_i2s_mp3.stream_active = 1;
audio_i2s_mp3.client = audio_i2s_mp3.MP3Server->client();
audio_i2s_mp3.stream_active = true;
audio_i2s_mp3.client = audio_i2s_mp3.StreamServer->client();
AddLog(LOG_LEVEL_INFO, PSTR("I2S: Create client"));
// i2s_record_shine((char*)"stream.mp3");
I2sRecordShine((char*)"stream.mp3");
}
void I2sMp3Loop(void) {
if (audio_i2s_mp3.MP3Server) {
audio_i2s_mp3.MP3Server->handleClient();
audio_i2s_mp3.use_stream = true;
if(I2sRecord((char*)"", audio_i2s_mp3.encoder_type) != pdTRUE){
AddLog(LOG_LEVEL_INFO, PSTR("I2S: Stop client"));
audio_i2s_mp3.stream_active = false;
audio_i2s_mp3.client.stop();
}
}
void I2sMp3Init(uint32_t on) {
void I2sStreamLoop(void) {
if (audio_i2s_mp3.StreamServer) {
audio_i2s_mp3.StreamServer->handleClient();
}
}
void I2sServerInit(uint32_t on) {
if (on) {
if (!audio_i2s_mp3.MP3Server) {
audio_i2s_mp3.MP3Server = new ESP8266WebServer(MP3_STREAM_PORT);
audio_i2s_mp3.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3);
audio_i2s_mp3.MP3Server->on(PSTR("/stream.m3a"), Stream_mp3);
audio_i2s_mp3.MP3Server->begin();
AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created on port: %d "), MP3_STREAM_PORT);
if (!audio_i2s_mp3.StreamServer) {
audio_i2s_mp3.StreamServer = new ESP8266WebServer(MP3_STREAM_PORT);
audio_i2s_mp3.StreamServer->on(PSTR("/stream.mp3"), I2SMP3StreamInit);
audio_i2s_mp3.StreamServer->on(PSTR("/stream.m3a"), I2SMP3StreamInit);
audio_i2s_mp3.StreamServer->on(PSTR("/stream.webm"), I2SOpusStreamInit);
audio_i2s_mp3.StreamServer->begin();
AddLog(LOG_LEVEL_INFO, PSTR("I2S: server created on port: %d "), MP3_STREAM_PORT);
}
} else {
if (audio_i2s_mp3.MP3Server) {
audio_i2s_mp3.MP3Server->stop();
delete audio_i2s_mp3.MP3Server;
audio_i2s_mp3.MP3Server = nullptr;
if (audio_i2s_mp3.StreamServer) {
audio_i2s_mp3.StreamServer->stop();
delete audio_i2s_mp3.StreamServer;
audio_i2s_mp3.StreamServer = nullptr;
audio_i2s_mp3.mic_stop = 1;
audio_i2s_mp3.stream_active = 0;
AddLog(LOG_LEVEL_INFO, PSTR("MP3: server deleted"));
audio_i2s_mp3.stream_active = false;
AddLog(LOG_LEVEL_INFO, PSTR("I2S: server deleted"));
}
}
}
@ -70,7 +85,7 @@ void CmndI2SMP3Stream(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
audio_i2s_mp3.stream_enable = XdrvMailbox.payload;
}
I2sMp3Init(audio_i2s_mp3.stream_enable);
I2sServerInit(audio_i2s_mp3.stream_enable);
ResponseCmndNumber(audio_i2s_mp3.stream_enable);
}