### Description

Adds opencv-python and ffmpeg.

### Status

opencv-python is modularized with a bunch of modules, I'll keep updating the status.

- [x] Core library
- [x] JPEG
- [x] PNG
- [x] WEBP
- [x] Image processing
- [x] video processing (FFMPEG)
- [x] objectdetect
- [x] dnn
- [x] ml
- [x] calib3d
- [x] feature2d
- [x] highgui (built, but nonsense)
- [x] photo
- [x] stitching
- [x] flann
- contrib algorithms?
- extra modules?

### Checklists

<!-- Note on checklists:
     If you think some of these steps are not necessary for your PR,
     just remove those checkboxes, or mark them as checked. Otherwise,
     if some checkboxes are left unmarked, we might assume that your PR
     is not ready to be merged and we might keep you waiting -->

- [x] Add a [CHANGELOG](https://github.com/pyodide/pyodide/blob/main/docs/project/changelog.md) entry
- [x] Add / update tests

Resolve #381
Resolve #1627
PKG Add opencv-python and ffmpeg. ### Description

Adds opencv-python and ffmpeg.

### Status

opencv-python is modularized with a bunch of modules, I'll keep updating the status.

- [x] Core library
- [x] JPEG
- [x] PNG
- [x] WEBP
- [x] Image processing
- [x] video processing (FFMPEG)
- [x] objectdetect
- [x] dnn
- [x] ml
- [x] calib3d
- [x] feature2d
- [x] highgui (built, but nonsense)
- [x] photo
- [x] stitching
- [x] flann
- contrib algorithms?
- extra modules?

### Checklists

<!-- Note on checklists:
     If you think some of these steps are not necessary for your PR,
     just remove those checkboxes, or mark them as checked. Otherwise,
     if some checkboxes are left unmarked, we might assume that your PR
     is not ready to be merged and we might keep you waiting -->

- [x] Add a [CHANGELOG](https://github.com/pyodide/pyodide/blob/main/docs/project/changelog.md) entry
- [x] Add / update tests

Resolve #381
Resolve #1627
def test_uncaught_cpp_exceptions(selenium):
    assert (
        selenium.run_js(
            """
            await pyodide.loadPackage("cpp-exceptions-test");
            const Tests = pyodide._api.tests;
            const sitePackages = pyodide.runPython("import site; site.getsitepackages()[0]")
            const idx = pyodide._module.LDSO.loadedLibNames[sitePackages + "/cpp-exceptions-test-throw.so"]
            const throwlib = pyodide._module.LDSO.loadedLibs[idx].module;
            """
            """\
            function t(x){
                try {
                    throwlib.throw_exc(x);
                } catch(e){
                    let errString = Tests.convertCppException(e).toString();
                    errString = errString.replace(/[0-9]+/, "xxx");
                    return errString;
                }
            }
            return [t(1), t(2), t(3), t(4), t(5)];
            """
        )
        == [
            "CppException int: The exception is an object of type int at address xxx "
            "which does not inherit from std::exception",
            "CppException char: The exception is an object of type char at address xxx "
            "which does not inherit from std::exception",
            "CppException std::runtime_error: abc",
            "CppException myexception: My exception happened",
            "CppException char const*: The exception is an object of type char const* at "
            "address xxx which does not inherit from std::exception",
        ]
    )


def test_cpp_exception_catching(selenium):
    assert (
        selenium.run_js(
            """
            await pyodide.loadPackage("cpp-exceptions-test");
            const Module = pyodide._module;
            const sitePackages = pyodide.runPython("import site; site.getsitepackages()[0]")
            const idx = Module.LDSO.loadedLibNames[sitePackages + "/cpp-exceptions-test-catch.so"]
            const catchlib = Module.LDSO.loadedLibs[idx].module;
            """
            """\
            function t(x){
                const ptr = catchlib.catch_exc(x);
                const res = Module.UTF8ToString(ptr);
                Module._free(ptr);
                return res;
            }

            return [t(1), t(2), t(3), t(5)];
            """
        )
        == [
            "caught int 1000",
            "caught char 99",
            "caught runtime_error abc",
            "caught ????",
        ]
    )

def test_uncaught_cpp_exceptions(selenium):
    assert (
        selenium.run_js(
            """
            await pyodide.loadPackage("cpp-exceptions-test");
            const Tests = pyodide._api.tests;
            const sitePackages = pyodide.runPython("import site; site.getsitepackages()[0]")
            const idx = pyodide._module.LDSO.loadedLibNames[sitePackages + "/cpp-exceptions-test-throw.so"]
            const throwlib = pyodide._module.LDSO.loadedLibs[idx].module;
            """
            """\
            function t(x){
                try {
                    throwlib.throw_exc(x);
                } catch(e){
                    let errString = Tests.convertCppException(e).toString();
                    errString = errString.replace(/[0-9]+/, "xxx");
                    return errString;
                }
            }
            return [t(1), t(2), t(3), t(4), t(5)];
            """
        )
        == [
            "CppException int: The exception is an object of type int at address xxx "
            "which does not inherit from std::exception",
            "CppException char: The exception is an object of type char at address xxx "
            "which does not inherit from std::exception",
            "CppException std::runtime_error: abc",
            "CppException myexception: My exception happened",
            "CppException char const*: The exception is an object of type char const* at "
            "address xxx which does not inherit from std::exception",
        ]
    )


def test_cpp_exception_catching(selenium):
    assert (
        selenium.run_js(
            """
            await pyodide.loadPackage("cpp-exceptions-test");
            const Module = pyodide._module;
            const sitePackages = pyodide.runPython("import site; site.getsitepackages()[0]")
            const idx = Module.LDSO.loadedLibNames[sitePackages + "/cpp-exceptions-test-catch.so"]
            const catchlib = Module.LDSO.loadedLibs[idx].module;
            """
            """\
            function t(x){
                const ptr = catchlib.catch_exc(x);
                const res = Module.UTF8ToString(ptr);
                Module._free(ptr);
                return res;
            }

            return [t(1), t(2), t(3), t(5)];
            """
        )
        == [
            "caught int 1000",
            "caught char 99",
            "caught runtime_error abc",
            "caught ????",
        ]
    )
