Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"", "gen_ut":1746414822, "reponame":"libwebsockets", "desc":"libwebsockets lightweight C networking library", "owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://libwebsockets.org/repo/libwebsockets", "f":3, "items": [ {"schema":"libjg2-1", "cid":"3077d04675bd78e2fdeef1d71a29d869", "commit": {"type":"commit", "time": 1620997318, "time_ofs": 60, "oid_tree": { "oid": "70235738f557b3fffcd7ba32ecac1d66da4d2a19", "alias": []}, "oid":{ "oid": "0a2922ced86b919f600601833b2e1d074bf46c58", "alias": [ "refs/heads/_v4l2"]}, "msg": "v4l2-video", "sig_commit": { "git_time": { "time": 1620997318, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }, "sig_author": { "git_time": { "time": 1617646988, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }}, "body": "v4l2-video\n" , "diff": "diff --git a/lib/plat/unix/unix-misc.c b/lib/plat/unix/unix-misc.c\nindex 8dcaaa9..71b0f8c 100644\n--- a/lib/plat/unix/unix-misc.c\n+++ b/lib/plat/unix/unix-misc.c\n@@ -48,13 +48,13 @@ lws_now_usecs(void)\n \tif (clock_gettime(CLOCK_MONOTONIC, \u0026ts))\n \t\treturn 0;\n \n-\treturn (((lws_usec_t)ts.tv_sec) * LWS_US_PER_SEC) +\n-\t\t\t((lws_usec_t)ts.tv_nsec / LWS_NS_PER_US);\n+\treturn (lws_usec_t)(((lws_usec_t)ts.tv_sec) * (lws_usec_t)LWS_US_PER_SEC) +\n+\t\t\t((lws_usec_t)ts.tv_nsec / (lws_usec_t)LWS_NS_PER_US);\n #else\n \tstruct timeval now;\n \n \tgettimeofday(\u0026now, NULL);\n-\treturn (((lws_usec_t)now.tv_sec) * LWS_US_PER_SEC) +\n+\treturn (((lws_usec_t)now.tv_sec) * (lws_usec_t)LWS_US_PER_SEC) +\n \t\t\t(lws_usec_t)now.tv_usec;\n #endif\n }\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/CMakeLists.txt b/minimal-examples/ws-server/minimal-ws-server-v4l2/CMakeLists.txt\nindex c45f06c..b2ccfcf 100644\n--- a/minimal-examples/ws-server/minimal-ws-server-v4l2/CMakeLists.txt\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/CMakeLists.txt\n@@ -6,19 +6,71 @@ include(CheckCSourceCompiles)\n include(LwsCheckRequirements)\n \n set(SAMP lws-minimal-ws-server-v4l2)\n-set(SRCS minimal-ws-server.c)\n+set(SRCS minimal-ws-server.c v4l2.c annex-b.c boxes.c transcode.c)\n \n set(requirements 1)\n require_lws_config(LWS_ROLE_WS 1 requirements)\n require_lws_config(LWS_WITH_SERVER 1 requirements)\n \n if (requirements AND ${CMAKE_SYSTEM_NAME} STREQUAL \u0022Linux\u0022)\n+\n+\tfind_package(PkgConfig REQUIRED)\n+\tpkg_check_modules(PC_FFMPEG REQUIRED IMPORTED_TARGET\n+\t\t\t\tlibavcodec libavformat libavutil libswscale)\n+\n+\tfind_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h\n+\t\t HINTS ${PC_FFMPEG_LIBAVFORMAT_INCLUDEDIR}\n+\t\t \t${PC_FFMPEG_INCLUDE_DIRS})\n+\n+\tfind_library(AVFORMAT_LIBRARY NAMES avformat\n+\t\t HINTS ${PC_FFMPEG_LIBAVFORMAT_LIBDIR}\n+\t\t \t${PC_FFMPEG_LIBRARY_DIRS})\n+\t \n+\tfind_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h\n+\t\t HINTS ${PC_FFMPEG_LIBAVCODEC_INCLUDEDIR}\n+\t\t \t${PC_FFMPEG_INCLUDE_DIRS})\n+\tfind_library(AVCODEC_LIBRARY NAMES avcodec\n+\t\t HINTS ${PC_FFMPEG_LIBAVCODEC_LIBDIR}\n+\t\t \t${PC_FFMPEG_LIBRARY_DIRS})\n+\t \n+\tfind_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h\n+\t\t HINTS ${PC_FFMPEG_LIBAVUTIL_INCLUDEDIR}\n+\t\t \t${PC_FFMPEG_INCLUDE_DIRS})\n+\tfind_library(AVUTIL_LIBRARY NAMES avutil\n+\t\t HINTS ${PC_FFMPEG_LIBAVUTIL_LIBDIR}\n+\t\t \t${PC_FFMPEG_LIBRARY_DIRS})\n+\t \n+\tfind_path(SWSCALE_INCLUDE_DIR libswscale/swscale.h\n+\t\t HINTS ${PC_FFMPEG_LIBSWSCALE_INCLUDEDIR}\n+\t\t \t${PC_FFMPEG_INCLUDE_DIRS})\n+\tfind_library(SWSCALE_LIBRARY NAMES swscale\n+\t\t HINTS ${PC_FFMPEG_LIBSWSCALE_LIBDIR}\n+\t\t \t${PC_FFMPEG_LIBRARY_DIRS})\n+\n \tadd_executable(${SAMP} ${SRCS})\n+\ttarget_include_directories(${SAMP} PUBLIC\n+\t\t\t\t ${AVFORMAT_INCLUDE_DIR}\n+\t\t\t\t ${AVCODEC_INCLUDE_DIR}\n+\t\t\t\t ${AVUTIL_INCLUDE_DIR}\n+\t\t\t\t ${SWSCALE_INCLUDE_DIR})\n+\n+\t# ffmpeg headers are not clean for these themselves\n+\ttarget_compile_options(${SAMP} PUBLIC -Wno-conversion -Wno-sign-conversion)\n \n \tif (websockets_shared)\n-\t\ttarget_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})\n+\t\ttarget_link_libraries(${SAMP} websockets_shared\n+\t\t\t\t\t${LIBWEBSOCKETS_DEP_LIBS}\n+\t\t\t\t\t${AVFORMAT_LIBRARY}\n+\t\t\t\t\t${AVCODEC_LIBRARY}\n+\t\t\t\t\t${SWSCALE_LIBRARY}\n+\t\t\t\t\t${AVUTIL_LIBRARY})\n \t\tadd_dependencies(${SAMP} websockets_shared)\n \telse()\n-\t\ttarget_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})\n+\t\ttarget_link_libraries(${SAMP} websockets\n+\t\t\t\t\t${LIBWEBSOCKETS_DEP_LIBS}\n+\t\t\t\t\t${AVFORMAT_LIBRARY}\n+\t\t\t\t\t${AVCODEC_LIBRARY}\n+\t\t\t\t\t${SWSCALE_LIBRARY}\n+\t\t\t\t\t${AVUTIL_LIBRARY})\n \tendif()\n-endif()\n\u005c No newline at end of file\n+endif()\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/annex-b.c b/minimal-examples/ws-server/minimal-ws-server-v4l2/annex-b.c\nnew file mode 100644\nindex 0000000..014e85e\n--- /dev/null\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/annex-b.c\n@@ -0,0 +1,193 @@\n+#include \u003clibwebsockets.h\u003e\n+\n+#include \u0022private.h\u0022\n+\n+#include \u003csignal.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cassert.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003cerrno.h\u003e\n+\n+enum {\n+\tIDLE,\n+\tSEEN_Z1,\n+\tSEEN_Z2,\n+\tHIT,\n+\n+\tNAL_IDR\t\t\u003d 5,\n+\tNAL_SPS\t\t\u003d 7,\n+\tNAL_PPS\t\t\u003d 8,\n+};\n+\n+/*\n+ * The h.264 stream itelf needs to have a set of NAL headers on each frame to\n+ * work in ffox.\n+ *\n+ * We have to parse out the SPS and PPS so we can provide them in the avcC box\n+ * at the mp4 layer.\n+ *\n+ * Some h.264 streams (eg, RPi Zero h264_omx) only have the IDR NAL on each\n+ * packet, they give the other NAL pieces only once in the stream just after it\n+ * was opened; this is not available to clients joining the stream later. To\n+ * further complicate it, the one-time NAL pieces are provided in the own\n+ * packet before any IDR content.\n+ *\n+ * We keep a copy of any h.264 coming before the avcc could be synthesized, if\n+ * we see that the subsequent h.264 packets don't have the SPS then we manually\n+ * apply the stored NAL back on to it before passing it for mp4 framing.\n+ */\n+\n+size_t\n+annex_b_scan(struct src_inst *si, uint8_t *data, size_t len)\n+{\n+\tuint8_t *p \u003d data, *end \u003d data + len;\n+\tuint8_t *w;\n+\n+\twhile (p \u003c end \u0026\u0026 si-\u003eab_type !\u003d NAL_IDR) {\n+\t\tswitch (si-\u003eab_state) {\n+\t\tcase IDLE:\n+\t\t\tif (!*p) {\n+\t\t\t\tsi-\u003eab_state \u003d SEEN_Z1;\n+\t\t\t\tsi-\u003eab_seq \u003d 1;\n+\t\t\t} else\n+\t\t\t\tsi-\u003eab_seq \u003d 0;\n+\t\t\tbreak;\n+\t\tcase SEEN_Z1:\n+\t\t\tif (!*p) {\n+\t\t\t\tsi-\u003eab_state \u003d SEEN_Z2;\n+\t\t\t\tsi-\u003eab_seq++;\n+\t\t\t} else {\n+\t\t\t\tsi-\u003eab_state \u003d IDLE;\n+\t\t\t\tsi-\u003eab_seq \u003d 0;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase SEEN_Z2:\n+\t\t\tif (!*p) {\n+\t\t\t\t/* absorb additional leading 00 */\n+\t\t\t\tsi-\u003eab_seq++;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (*p !\u003d 1) {\n+\t\t\t\tsi-\u003eab_state \u003d IDLE;\n+\t\t\t\tsi-\u003eab_seq \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tsi-\u003eab_state \u003d HIT;\n+\t\t\tsi-\u003eab_seq++;\n+\t\t\tbreak;\n+\t\tcase HIT:\n+\n+\t\t\tswitch (si-\u003eab_type) {\n+\t\t\tcase 7:\n+\t\t\t\tsi-\u003eh264_sps_len -\u003d si-\u003eab_seq;\n+\t\t\t\tbreak;\n+\t\t\tcase 8:\n+\t\t\t\tsi-\u003eh264_pps_len -\u003d si-\u003eab_seq;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tsi-\u003eab_type \u003d (*p++) \u0026 0x1f;\n+\n+\t\t\tswitch (si-\u003eab_type) {\n+\t\t\tcase NAL_IDR:\n+\t\t\t\tsi-\u003eavcc_header_len \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\tcase NAL_SPS:\n+\t\t\t\tsi-\u003eh264_sps_len \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\tcase NAL_PPS:\n+\t\t\t\tsi-\u003eh264_pps_len \u003d 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tsi-\u003eab_state \u003d IDLE;\n+\t\t\tsi-\u003eab_seq \u003d 0;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tswitch (si-\u003eab_type) {\n+\t\tcase NAL_SPS:\n+\t\t\tif (si-\u003eh264_sps_len \u003c sizeof(si-\u003eh264_sps))\n+\t\t\t\tsi-\u003eh264_sps[si-\u003eh264_sps_len++] \u003d *p;\n+\t\t\tbreak;\n+\t\tcase NAL_PPS:\n+\t\t\tif (si-\u003eh264_pps_len \u003c sizeof(si-\u003eh264_pps))\n+\t\t\t\tsi-\u003eh264_pps[si-\u003eh264_pps_len++] \u003d *p;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tp++;\n+\t}\n+\n+\tif (!si-\u003eavcc_header_len \u0026\u0026 si-\u003eh264_sps_len \u0026\u0026 si-\u003eh264_pps_len \u0026\u0026\n+\t si-\u003eab_type \u003d\u003d NAL_IDR) {\n+\t\t/*\n+\t\t * Cook up the AVCC from the extracted Appendix B pieces\n+\t\t */\n+\n+\t\tw \u003d si-\u003eavcc_header + LWS_PRE;\n+\t\t*w++ \u003d 0x01;\n+\t\t*w++ \u003d si-\u003eh264_sps[0];\n+\t\t*w++ \u003d si-\u003eh264_sps[1];\n+\t\t*w++ \u003d si-\u003eh264_sps[2];\n+\t\t*w++ \u003d 0xff;\n+\t\t*w++ \u003d 0xe1;\n+\n+\t\tlws_ser_wu16be(w, si-\u003eh264_sps_len + 0);\n+\t\tw +\u003d 2;\n+\t//\t*w++ \u003d 0x67;\n+\t\tmemcpy(w, si-\u003eh264_sps, si-\u003eh264_sps_len);\n+\t\tw +\u003d si-\u003eh264_sps_len;\n+\n+\t\t*w++ \u003d 0x01;\n+\n+\t\tlws_ser_wu16be(w, si-\u003eh264_pps_len + 0);\n+\t\tw +\u003d 2;\n+\t//\t*w++ \u003d 0x68;\n+\t\tmemcpy(w, si-\u003eh264_pps, si-\u003eh264_pps_len);\n+\t\tw +\u003d si-\u003eh264_pps_len;\n+\n+\t\tsi-\u003eavcc_header_len \u003d lws_ptr_diff_size_t(w,\n+\t\t\t\t\t\tsi-\u003eavcc_header + LWS_PRE);\n+\n+\t\t//lwsl_notice(\u0022%s: AVCC:\u005cn\u0022, __func__);\n+\t\t//lwsl_hexdump_notice(si-\u003eavcc_header + LWS_PRE,\n+\t\t//\t\t si-\u003eavcc_header_len);\n+\n+\t\t/* append it as formal extradata */\n+\n+\t\tif (!si-\u003emp4_header_len)\n+\t\t\tmp4_header_prepare(si);\n+\n+\t\tif (!si-\u003eavcc_e-\u003eextradata) {\n+\t\t\tsi-\u003eavcc_e-\u003eextradata \u003d (uint8_t *)av_mallocz(\n+\t\t\t\t\t\t\tsi-\u003eavcc_header_len);\n+\t\t\tif (si-\u003eavcc_e-\u003eextradata) {\n+\t\t\t\tmemcpy(si-\u003eavcc_e-\u003eextradata,\n+\t\t\t\t si-\u003eavcc_header + LWS_PRE,\n+\t\t\t\t\t\t\tsi-\u003eavcc_header_len);\n+\t\t\t\tsi-\u003eavcc_e-\u003eextradata_size \u003d si-\u003eavcc_header_len;\n+\t\t\t}\n+\n+\t\t\tsi-\u003eavs_e-\u003ecodecpar-\u003eextradata \u003d (uint8_t *)av_mallocz(\n+\t\t\t\t\t\tsi-\u003eavcc_header_len +\n+\t\t\t\t\t\tAV_INPUT_BUFFER_PADDING_SIZE);\n+\t\t\tif (si-\u003eavs_e-\u003ecodecpar-\u003eextradata) {\n+\t\t\t\tmemcpy(si-\u003eavs_e-\u003ecodecpar-\u003eextradata,\n+\t\t\t\t si-\u003eavcc_header + LWS_PRE,\n+\t\t\t\t si-\u003eavcc_header_len);\n+\t\t\t\tsi-\u003eavs_e-\u003ecodecpar-\u003eextradata_size \u003d\n+\t\t\t\t\t\t\tsi-\u003eavcc_header_len;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (si-\u003eab_type \u003d\u003d NAL_IDR)\n+\t\tsi-\u003eab_type \u003d 0;\n+\n+\treturn lws_ptr_diff_size_t(p, data);\n+}\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/boxes.c b/minimal-examples/ws-server/minimal-ws-server-v4l2/boxes.c\nnew file mode 100644\nindex 0000000..2a44872\n--- /dev/null\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/boxes.c\n@@ -0,0 +1,730 @@\n+/*\n+ * lws-minimal-ws-server-v4l2\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+\n+#include \u0022private.h\u0022\n+\n+#define PACK __attribute__((packed))\n+\n+\n+typedef struct bctx {\n+\tuint8_t\t\t*p, *end;\n+\tu32be_t\t\t*stack[8];\n+\tint\t\tsp;\n+} PACK bctx_t;\n+\n+typedef struct box {\n+\tu32be_t\t\tlen;\n+\tchar\t\tname[4];\n+} PACK box_t;\n+\n+typedef struct fbox {\n+\tbox_t\t\tbox;\n+\tuint8_t\t\tver;\n+\tuint8_t\t\tflags[3];\n+} PACK fbox_t;\n+\n+typedef struct box_ftyp1 {\n+\tbox_t\t\tbox;\n+\tchar\t\tname[4];\n+\tuint8_t\t\tmiver[4];\n+\tchar\t\tname1[8];\n+} PACK ftyp1_t;\n+\n+typedef struct fbox_mvhd {\n+\tfbox_t\t\tfbox;\n+\tu32be_t\t\tctime;\n+\tu32be_t\t\tmtime;\n+\tu32be_t\t\ttimescale;\n+\tu32be_t\t\tduration;\n+\n+\tu32be_t\t\trate;\n+\tu16be_t\t\tvol_full;\n+\tu16be_t\t\treserved1;\n+\tu32be_t\t\treserved2[2];\n+\n+\tu32be_t\t\tmatrix[9];\n+\n+\tu32be_t\t\tpredef[6];\n+\tu32be_t\t\tnext_track_id;\n+} PACK fbox_mvhd_t;\n+\n+typedef struct fbox_trex {\n+\tfbox_t\t\tfbox;\n+\tu32be_t\t\ttrack_id;\n+\tu32be_t\t\tdef_samp_dec_idx;\n+\tu32be_t\t\tdef_samp_duration;\n+\tu32be_t\t\tdef_samp_size;\n+\tu32be_t\t\tdef_samp_flags;\n+} PACK trex_t;\n+\n+typedef struct fbox_tkhd {\n+\tfbox_t\t\tfbox; /* v1 layout */\n+\n+\tu64be_t\t\tctime;\n+\tu64be_t\t\tmtime;\n+\tu32be_t\t\ttrack_id;\n+\tu32be_t\t\treserved;\n+\tu64be_t\t\ttrack_length;\n+\n+\tu32be_t\t\treserved2[2];\n+\n+\tu16be_t\t\tlayer;\n+\tu16be_t\t\talt_group;\n+\tu16be_t\t\tvolume;\n+\tu16be_t\t\treserved3;\n+\n+\tu32be_t\t\tmatrix[9];\n+\n+\tu32be_t\t\twidth;\n+\tu32be_t\t\theight;\n+} PACK fbox_tkhd_t;\n+\n+typedef struct fbox_stsd {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tentry_count;\n+\n+\t/* variable */\n+} PACK fbox_stsd_t;\n+\n+typedef struct fbox_stsz {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tsample_size;\n+\tu32be_t\t\tentry_count;\n+\n+\t/* variable */\n+} PACK fbox_stsz_t;\n+\n+typedef struct box_avc1 {\n+\tbox_t\t\tbox;\n+\n+\tu16be_t\t\treserved[3];\n+\tu16be_t\t\tdata_reference_index;\n+\n+\tu16be_t\t\treserved1;\n+\tu16be_t\t\treserved2;\n+\tu32be_t\t\treserved3[3];\n+\n+\tu16be_t\t\twidth;\n+\tu16be_t\t\theight;\n+\n+\tu32be_t\t\thres;\n+\tu32be_t\t\tvres;\n+\n+\tu32be_t\t\treserved4;\n+\tu16be_t\t\tframe_count;\n+\n+\tuint8_t\t\tslen;\n+\tuint8_t\t\tcompname[31];\n+\n+\tu16be_t\t\tdepth;\n+\tu16be_t\t\tffff;\n+\n+} PACK avc1_t;\n+\n+typedef struct box_avcC {\n+\tbox_t\t\tbox;\n+\n+\tuint8_t\t\tversion;\n+\tuint8_t\t\tavc_profile_ind;\n+\tuint8_t\t\tavc_profile_comp;\n+\tuint8_t\t\tavc_level_ind;\n+\tuint8_t\t\tlength_minus_one;\n+\tuint8_t\t\tnum_sps;\n+\n+\t/*\n+\t * SPS size + SPS, then uint8_t num_pps, then PPS size + PPS\n+\t */\n+\n+} PACK box_avcC_t;\n+\n+typedef struct box_uuid_tfxd {\n+\tbox_t\t\tbox;\n+\n+\tuint8_t\t\tuuid[16];\n+\tuint8_t\t\tversion;\n+\tuint8_t\t\trsv[3];\n+\tu64be_t\t\tfrag_abs_time;\n+\tu64be_t\t\tfrag_dur;\n+\n+} PACK box_uuid_tfxd_t;\n+\n+\n+typedef struct fbox_mdhd {\n+\tfbox_t\t\tfbox; /* v1 */\n+\n+\tu64be_t\t\tctime;\n+\tu64be_t\t\tmtime;\n+\tu32be_t\t\ttimescale;\n+\tu64be_t\t\tduration;\n+\n+\tu16be_t\t\tlang;\n+\tu16be_t\t\tpre_defined;\n+\n+} PACK fbox_mdhd_t;\n+\n+typedef struct fbox_hdlr {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tpredefined;\n+\tchar\t\thandler[4];\n+\tu32be_t\t\treserved[3];\n+\n+\t/* str with terminating NUL here */\n+\n+} PACK fbox_hdlr_t;\n+\n+typedef struct fbox_tfhd {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\ttrack_id;\n+} PACK fbox_tfhd_t;\n+\n+typedef struct fbox_tfhd_dsf {\n+\tfbox_t\t\tfbox; /* for flags 0x20 */\n+\n+\tu32be_t\t\ttrack_id;\n+//\tu32be_t\t\tdefault_sample_dur;\n+\tu32be_t\t\tdefault_sample_flags;\n+} PACK fbox_tfhd_dsf_t;\n+\n+typedef struct fbox_tfdt {\n+\tfbox_t\t\tfbox; /* version 1 */\n+\n+\tu64be_t\t\tbase_media_decode_time;\n+} PACK fbox_tfdt_t;\n+\n+typedef struct fbox_trun {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tsample_count;\n+\tu32be_t\t\tdata_offset;\n+\tu32be_t\t\tfirst_sample_flags;\n+\tu32be_t\t\tsample_duration;\n+\tu32be_t\t\tsample_size;\n+} PACK fbox_trun_t;\n+\n+typedef struct fbox_trun_subseq {\n+\tfbox_t\t\tfbox;\t/* flabs 000305 */\n+\n+\tu32be_t\t\tsample_count;\n+\tu32be_t\t\tdata_offset;\n+\tu32be_t\t\tfirst_sample_flags;\n+\tu32be_t\t\tsample_duration;\n+\tu32be_t\t\tsample_size;\n+//\tu32be_t\t\tsample_composition_time_offset;\n+} PACK fbox_trun_subseq_t;\n+\n+typedef struct box_mfhd {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tfrag_seq;\n+} PACK box_mfhd_t;\n+\n+typedef struct fbox_vmhd {\n+\tfbox_t\t\tfbox;\n+\n+\tu16be_t\t\tgfx_mode;\n+\tu16be_t\t\tgfx_color[3];\n+} PACK fbox_vmhd_t;\n+\n+typedef struct fbox_dref {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tentry_count;\n+} PACK fbox_dref_t;\n+\n+typedef struct fbox_url {\n+\tfbox_t\t\tfbox;\n+\n+\t/* string + NUL */\n+} PACK fbox_url_t;\n+\n+typedef struct fbox_mehd {\n+\tfbox_t\t\tfbox;\n+\n+\tu32be_t\t\tfragment_dur;\n+} PACK fbox_mehd_t;\n+\n+#define STACK_PUSH\t\t1\n+#define STAY\t\t\t0\n+\n+/* handles unaligned and BE */\n+void\n+add_u32be(u32be_t *dest, unsigned int add_le)\n+{\n+\tuint8_t *p \u003d (uint8_t *)dest;\n+\tuint32_t src \u003d (p[0] \u003c\u003c 24) | (p[1] \u003c\u003c 16) | (p[2] \u003c\u003c 8) | p[3], r;\n+\n+\tr \u003d src + add_le;\n+\n+\tp[0] \u003d (r \u003e\u003e 24) \u0026 0xff;\n+\tp[1] \u003d (r \u003e\u003e 16) \u0026 0xff;\n+\tp[2] \u003d (r \u003e\u003e 8) \u0026 0xff;\n+\tp[3] \u003d (r) \u0026 0xff;\n+}\n+\n+static void\n+prep_box(box_t *box, const char *name, size_t len)\n+{\n+\tmemset(box, 0, len);\n+\tbox-\u003elen \u003d htonl(len);\n+\tbox-\u003ename[0] \u003d name[0];\n+\tbox-\u003ename[1] \u003d name[1];\n+\tbox-\u003ename[2] \u003d name[2];\n+\tbox-\u003ename[3] \u003d name[3];\n+}\n+\n+static int\n+write_box(bctx_t *bcx, const box_t *box, int push, size_t bs, size_t adv)\n+{\n+\tint n;\n+\n+\tif (push)\n+\t\tbcx-\u003estack[bcx-\u003esp] \u003d (u32be_t *)bcx-\u003ep;\n+\n+\tif (bs \u003e lws_ptr_diff_size_t(bcx-\u003eend, bcx-\u003ep))\n+\t\treturn -1;\n+\n+\tmemcpy(bcx-\u003ep, box, bs);\n+\tbcx-\u003ep +\u003d bs + adv;\n+\n+\tfor (n \u003d 0; n \u003c bcx-\u003esp; n++)\n+\t\tadd_u32be(bcx-\u003estack[n], (int)(bs + adv));\n+\n+\tif (push)\n+\t\tbcx-\u003esp++;\n+\n+\treturn 0;\n+}\n+\n+int\n+mp4_header_prepare(struct src_inst *si)\n+{\n+\tbctx_t\tbcx;\n+\tuint8_t *start \u003d si-\u003emp4_hdr + LWS_PRE, *p;\n+\tftyp1_t\t\tf1;\n+\tbox_t\t\tmoov, mvex, trak, mdia, minf, stbl, dinf;\n+//\tfbox_t\t\tnmhd;\n+\tfbox_mvhd_t\tmvhd;\n+\tfbox_tkhd_t\ttkhd;\n+\tfbox_stsd_t\tstsd;\n+\tfbox_mdhd_t\tmdhd;\n+\tfbox_hdlr_t\thdlr;\n+\ttrex_t\t\ttrex;\n+\tavc1_t\t\tavc1;\n+\tbox_avcC_t\tavcc;\n+\tfbox_vmhd_t\tvmhd;\n+\tfbox_dref_t\tdref;\n+\tfbox_url_t\turl;\n+\tfbox_mehd_t\tmehd;\n+\tfbox_stsz_t\tstsz;\n+\n+\tbcx.p \u003d start;\n+\tbcx.end \u003d start + sizeof(si-\u003emp4_hdr) - LWS_PRE;\n+\tbcx.sp \u003d 0;\n+\n+\t/* ftyp */\n+\n+\tprep_box(\u0026f1.box, \u0022styp\u0022, sizeof(f1));\n+\n+\tf1.miver[2]\t\t\t\u003d 2;\n+\n+\tf1.name[0]\t\t\t\u003d 'm';\n+\tf1.name[1]\t\t\t\u003d 'p';\n+\tf1.name[2]\t\t\t\u003d '4';\n+\tf1.name[3]\t\t\t\u003d '1';\n+\n+\tf1.name1[0]\t\t\t\u003d 'i';\n+\tf1.name1[1]\t\t\t\u003d 's';\n+\tf1.name1[2]\t\t\t\u003d 'o';\n+\tf1.name1[3]\t\t\t\u003d 'm';\n+\tf1.name1[4]\t\t\t\u003d 'a';\n+\tf1.name1[5]\t\t\t\u003d 'v';\n+\tf1.name1[6]\t\t\t\u003d 'c';\n+\tf1.name1[7]\t\t\t\u003d '1';\n+\n+\twrite_box(\u0026bcx, \u0026f1.box, 0, sizeof(f1), 0);\n+\n+\t/* moov */\n+\n+\tprep_box(\u0026moov, \u0022moov\u0022, sizeof(moov));\n+\twrite_box(\u0026bcx, \u0026moov, STACK_PUSH, sizeof(moov), 0);\n+\n+\t/* moov -\u003e mvhd */\n+\n+\tprep_box(\u0026mvhd.fbox.box, \u0022mvhd\u0022, sizeof(mvhd));\n+\tmvhd.ctime\t\t\t\u003d htonl(0);\n+\tmvhd.mtime\t\t\t\u003d htonl(0);\n+\tmvhd.timescale\t\t\t\u003d htonl(1000);\n+\tmvhd.duration\t\t\t\u003d htonl(0);\n+\tmvhd.rate\t\t\t\u003d htonl(1 \u003c\u003c 16);\n+\tmvhd.vol_full\t\t\t\u003d htons(1 \u003c\u003c 8);\n+\tmvhd.matrix[0]\t\t\t\u003d htonl(0x00010000);\n+\tmvhd.matrix[4]\t\t\t\u003d htonl(0x00010000);\n+\tmvhd.matrix[8]\t\t\t\u003d htonl(0x40000000);\n+\tmvhd.next_track_id\t\t\u003d htonl(2);\n+\twrite_box(\u0026bcx, \u0026mvhd.fbox.box, STAY, sizeof(mvhd), 0);\n+\n+\t/* moov -\u003e trak */\n+\n+\tprep_box(\u0026trak, \u0022trak\u0022, sizeof(trak));\n+\twrite_box(\u0026bcx, \u0026trak, STACK_PUSH, sizeof(trak), 0);\n+\n+\t/* moov -\u003e trak -\u003e tkhd */\n+\n+\tprep_box(\u0026tkhd.fbox.box, \u0022tkhd\u0022, sizeof(tkhd));\n+\ttkhd.fbox.ver\t\t\t\u003d 1;\n+\ttkhd.fbox.flags[2]\t\t\u003d 3;\n+\tlws_ser_wu64be((uint8_t *)\u0026tkhd.ctime, 0);\n+\tlws_ser_wu64be((uint8_t *)\u0026tkhd.mtime, 0);\n+\ttkhd.track_id\t\t\t\u003d htonl(1);\n+\tlws_ser_wu64be((uint8_t *)\u0026tkhd.track_length, 0xffffffffffffffffULL);\n+\n+\ttkhd.layer\t\t\t\u003d htons(0);\n+\ttkhd.alt_group\t\t\t\u003d htons(0);\n+\ttkhd.volume\t\t\t\u003d htons(0);\n+\n+\ttkhd.matrix[0]\t\t\t\u003d htonl(0x00010000);\n+\ttkhd.matrix[4]\t\t\t\u003d htonl(0x00010000);\n+\ttkhd.matrix[8]\t\t\t\u003d htonl(0x40000000);\n+\n+\ttkhd.width\t\t\t\u003d htonl(si-\u003ewidth \u003c\u003c 16);\n+\ttkhd.height\t\t\t\u003d htonl(si-\u003eheight \u003c\u003c 16);\n+\twrite_box(\u0026bcx, \u0026tkhd.fbox.box, STAY, sizeof(tkhd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia */\n+\n+\tprep_box(\u0026mdia, \u0022mdia\u0022, sizeof(mdia));\n+\twrite_box(\u0026bcx, \u0026mdia, STACK_PUSH, sizeof(mdia), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e mdhd */\n+\n+\tprep_box(\u0026mdhd.fbox.box, \u0022mdhd\u0022, sizeof(mdhd));\n+\tmdhd.fbox.ver\t\t\t\u003d 1;\n+\tlws_ser_wu64be((uint8_t *)\u0026mdhd.ctime, 0);\n+\tlws_ser_wu64be((uint8_t *)\u0026mdhd.mtime, 0);\n+\tmdhd.timescale\t\t\t\u003d htonl(0x989680);\n+\tlws_ser_wu64be((uint8_t *)\u0026mdhd.duration, 0xffffffffffffffffULL);\n+\tmdhd.lang\t\t\t\u003d htons(0x55c4);\n+\twrite_box(\u0026bcx, \u0026mdhd.fbox.box, STAY, sizeof(mdhd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e hdlr */\n+\n+\tprep_box(\u0026hdlr.fbox.box, \u0022hdlr\u0022, sizeof(hdlr) + strlen(\u0022VideoHandler\u0022) + 1);\n+\thdlr.handler[0]\t\t\t\u003d 'v';\n+\thdlr.handler[1]\t\t\t\u003d 'i';\n+\thdlr.handler[2]\t\t\t\u003d 'd';\n+\thdlr.handler[3]\t\t\t\u003d 'e';\n+\n+\tp \u003d bcx.p + sizeof(hdlr);\n+\n+\twrite_box(\u0026bcx, \u0026hdlr.fbox.box, 0, sizeof(hdlr), strlen(\u0022VideoHandler\u0022) + 1);\n+\n+\tmemcpy(p, \u0022VideoHandler\u0022, strlen(\u0022VideoHandler\u0022) + 1);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf */\n+\n+\tprep_box(\u0026minf, \u0022minf\u0022, sizeof(minf));\n+\twrite_box(\u0026bcx, \u0026minf, STACK_PUSH, sizeof(minf), 0);\n+\n+\t/*\n+\t * moov -\u003e trak -\u003e mdia -\u003e minf -\u003e vmhd\n+\t *\n+\t * shaka requires this, ffox / vlc /mplayer don't care\n+\t */\n+\n+\tprep_box(\u0026vmhd.fbox.box, \u0022vmhd\u0022, sizeof(vmhd));\n+\tvmhd.fbox.flags[2]\t\t\u003d 1;\n+\twrite_box(\u0026bcx, \u0026vmhd.fbox.box, 0, sizeof(vmhd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e dinf */\n+\n+\tprep_box(\u0026dinf, \u0022dinf\u0022, sizeof(dinf));\n+\twrite_box(\u0026bcx, \u0026dinf, STACK_PUSH, sizeof(dinf), 0);\n+\n+\t/*\n+\t * moov -\u003e trak -\u003e mdia -\u003e minf -\u003e dinf -\u003e dref\n+\t *\n+\t * shaka requires this, ffox / vlc /mplayer don't care\n+\t */\n+\n+\tprep_box(\u0026dref.fbox.box, \u0022dref\u0022, sizeof(dref));\n+\tdref.entry_count\t\t\u003d htonl(1);\n+\twrite_box(\u0026bcx, \u0026dref.fbox.box, STACK_PUSH, sizeof(dref), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e dinf -\u003e dref -\u003e url\n+\t *\n+\t * If the flag is set indicating that the data is in the same file as\n+\t * this box, then no string (not even an empty one) shall be supplied\n+\t * in the entry field.\n+\t */\n+\n+\tprep_box(\u0026url.fbox.box, \u0022url \u0022, sizeof(url));\n+\turl.fbox.flags[2]\t\t\u003d 1;\n+//\tp \u003d bcx.p + sizeof(url);\n+\twrite_box(\u0026bcx, \u0026url.fbox.box, STAY, sizeof(url), 0); //1);\n+//\t*p \u003d 0;\n+\n+\tbcx.sp \u003d 4;\n+#if 0\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e nmhd */\n+\n+\tprep_box(\u0026nmhd.box, \u0022nmhd\u0022, sizeof(nmhd));\n+\twrite_box(\u0026bcx, \u0026nmhd.box, STAY, sizeof(nmhd), 0);\n+#endif\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl */\n+\n+\tprep_box(\u0026stbl, \u0022stbl\u0022, sizeof(stbl));\n+\twrite_box(\u0026bcx, \u0026stbl, STACK_PUSH, sizeof(stbl), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stsd */\n+\n+\tprep_box(\u0026stsd.fbox.box, \u0022stsd\u0022, sizeof(stsd));\n+\tstsd.entry_count\t\t\u003d htonl(1);\n+\twrite_box(\u0026bcx, \u0026stsd.fbox.box, STACK_PUSH, sizeof(stsd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stsd -\u003e avc1 */\n+\n+\tprep_box(\u0026avc1.box, \u0022avc1\u0022, sizeof(avc1));\n+\tavc1.data_reference_index\t\u003d htons(1);\n+\tavc1.width\t\t\t\u003d htons(si-\u003ewidth);\n+\tavc1.height\t\t\t\u003d htons(si-\u003eheight);\n+\tavc1.hres\t\t\t\u003d htonl(0x00480000);\n+\tavc1.vres\t\t\t\u003d htonl(0x00480000);\n+\tavc1.frame_count\t\t\u003d htons(1);\n+\tavc1.slen\t\t\t\u003d 3;\n+\tavc1.compname[0]\t\t\u003d 'l';\n+\tavc1.compname[1]\t\t\u003d 'w';\n+\tavc1.compname[2]\t\t\u003d 's';\n+\tavc1.depth\t\t\t\u003d htons(24);\n+\tavc1.ffff\t\t\t\u003d htons(0xffff);\n+\twrite_box(\u0026bcx, \u0026avc1.box, STACK_PUSH, sizeof(avc1), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stsd -\u003e avc1 -\u003e avcC */\n+\n+\tprep_box(\u0026avcc.box, \u0022avcC\u0022, sizeof(avcc) + 3 + si-\u003eh264_sps_len +\n+\t\t\t 1 + 3 + si-\u003eh264_pps_len);\n+\tavcc.version\t\t\t\u003d 1;\n+\tavcc.avc_profile_ind\t\t\u003d si-\u003eh264_sps[0];\n+\tavcc.avc_profile_comp\t\t\u003d si-\u003eh264_sps[1];\n+\tavcc.avc_level_ind\t\t\u003d si-\u003eh264_sps[2];\n+\n+\tavcc.length_minus_one\t\t\u003d 0xfc | (4 - 1);\n+\tavcc.num_sps\t\t\t\u003d 0xe0 | 1;\n+\n+\t/* we need to place spslen + sps, 0x01, ppslen + pps here */\n+\n+\tp \u003d bcx.p + sizeof(avcc);\n+\n+\twrite_box(\u0026bcx, \u0026avcc.box, STAY, sizeof(avcc), 3 + si-\u003eh264_sps_len +\n+\t\t\t\t\t 1 + 3 + si-\u003eh264_pps_len);\n+\n+\t/* these are fixed 16-bit lengths not subject to length_minus_one */\n+\tlws_ser_wu16be(p, si-\u003eh264_sps_len + 1);\n+\tp +\u003d 2;\n+\t*p++ \u003d 0x67;\n+\n+\tmemcpy(p, si-\u003eh264_sps, si-\u003eh264_sps_len);\n+\tp +\u003d si-\u003eh264_sps_len;\n+\n+\t*p++\t\t\t\t\u003d 1; /* number of pps blobs */\n+\n+\t/* these are fixed 16-bit lengths not subject to length_minus_one */\n+\tlws_ser_wu16be(p, si-\u003eh264_pps_len + 1);\n+\tp +\u003d 2;\n+\t*p++ \u003d 0x68;\n+\n+\tmemcpy(p, si-\u003eh264_pps, si-\u003eh264_pps_len);\n+\tp +\u003d si-\u003eh264_pps_len;\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stts */\n+\n+\tbcx.sp \u003d 5; /* back to stbl */\n+\n+\tprep_box(\u0026stsd.fbox.box, \u0022stts\u0022, sizeof(stsd));\n+\tstsd.entry_count\t\t\u003d htonl(0);\n+\twrite_box(\u0026bcx, \u0026stsd.fbox.box, STAY, sizeof(stsd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stsc */\n+\n+\tprep_box(\u0026stsd.fbox.box, \u0022stsc\u0022, sizeof(stsd));\n+\tstsd.entry_count\t\t\u003d htonl(0);\n+\twrite_box(\u0026bcx, \u0026stsd.fbox.box, STAY, sizeof(stsd), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stsz */\n+\n+\tprep_box(\u0026stsz.fbox.box, \u0022stsz\u0022, sizeof(stsz));\n+\tstsz.sample_size\t\t\u003d htonl(0);\n+\tstsz.entry_count\t\t\u003d htonl(0);\n+\twrite_box(\u0026bcx, \u0026stsz.fbox.box, STAY, sizeof(stsz), 0);\n+\n+\t/* moov -\u003e trak -\u003e mdia -\u003e minf -\u003e stbl -\u003e stco */\n+\n+\tprep_box(\u0026stsd.fbox.box, \u0022stco\u0022, sizeof(stsd));\n+\tstsd.entry_count\t\t\u003d htonl(0);\n+\twrite_box(\u0026bcx, \u0026stsd.fbox.box, STAY, sizeof(stsd), 0);\n+\n+\tbcx.sp \u003d 1; /* back to moov */\n+\n+\t/* moov -\u003e mvex */\n+\n+\tprep_box(\u0026mvex, \u0022mvex\u0022, sizeof(mvex));\n+\twrite_box(\u0026bcx, \u0026mvex, STACK_PUSH, sizeof(mvex), 0);\n+\n+\t/* moov -\u003e mvex -\u003e mehd */\n+\n+\tprep_box(\u0026mehd.fbox.box, \u0022mehd\u0022, sizeof(mehd));\n+\tmehd.fragment_dur\t\t\u003d htonl(0);\n+\twrite_box(\u0026bcx, \u0026mehd.fbox.box, STAY, sizeof(mehd), 0);\n+\n+\t/* moov -\u003e mvex -\u003e trex */\n+\n+\tprep_box(\u0026trex.fbox.box, \u0022trex\u0022, sizeof(trex));\n+\ttrex.track_id\t\t\t\u003d htonl(1);\n+\ttrex.def_samp_dec_idx\t\t\u003d htonl(1);\n+\twrite_box(\u0026bcx, \u0026trex.fbox.box, STAY, sizeof(trex), 0);\n+\n+\tbcx.sp--; /* pop mvex */\n+\n+\tsi-\u003emp4_header_len \u003d lws_ptr_diff_size_t(bcx.p, start);\n+\n+\tlwsl_hexdump_notice(si-\u003emp4_hdr + LWS_PRE, si-\u003emp4_header_len);\n+\n+\treturn 0;\n+}\n+\n+static const uint8_t wellknown_uuid_tfxd[] \u003d {\n+\t0x6D, 0x1D, 0x9B, 0x05, 0x42, 0xD5, 0x44, 0xE6,\n+\t0x80, 0xE2, 0x14, 0x1D, 0xAF, 0xF7, 0x57, 0xB2\n+};\n+\n+size_t\n+moof_prepare(struct src_inst *si, uint8_t *start, struct pss *pss,\n+\t size_t max, size_t vlen, unsigned int frag_dur)\n+{\n+\tbox_t\t\t\tmoof, traf, mdat;\n+\tbox_mfhd_t\t\tmfhd;\n+//\tfbox_tfhd_t\t\ttfhd;\n+\tfbox_tfhd_dsf_t\t\ttfhd_dsf;\n+//\tfbox_trun_t\t\ttrun;\n+\tfbox_trun_subseq_t\ttruns;\n+\tfbox_tfdt_t\t\ttfdt;\n+\tbctx_t\t\t\tbcx;\n+\tuint8_t\t\t\t*p, *datum;\n+\tu32be_t\t\t\t*ofs \u003d NULL;\n+\tbox_uuid_tfxd_t\t\tuuid_tfxd;\n+\n+\tdatum \u003d bcx.p \u003d start;\n+\tbcx.end \u003d start + max;\n+\tbcx.sp \u003d 0;\n+\n+\t/*\n+\t * Supposedly mandatory structure\n+\t *\n+\t * moof [ mfhd, traf [ tfhd, trik, trun ] ]\n+\t */\n+\n+\tprep_box(\u0026moof, \u0022moof\u0022, sizeof(moof));\n+\twrite_box(\u0026bcx, \u0026moof, STACK_PUSH, sizeof(moof), 0);\n+\n+\t/* moof- -\u003e mfhd */\n+\n+\tprep_box(\u0026mfhd.fbox.box, \u0022mfhd\u0022, sizeof(mfhd));\n+\tpss-\u003eseq++;\n+\tmfhd.frag_seq\t\t\t\u003d htonl(pss-\u003eseq);\n+\twrite_box(\u0026bcx, \u0026mfhd.fbox.box, STAY, sizeof(mfhd), 0);\n+\n+\t/* moof -\u003e traf */\n+\n+\t/*\n+\t * The base‐data‐offset for the first track in the movie fragment is the\n+\t * position of the first byte of the enclosing Movie Fragment Box, and\n+\t * for second and subsequent track fragments, the default is the end of\n+\t * the data defined by the preceding track fragment.\n+\t */\n+\tif (pss-\u003esent_initial_moof)\n+\t\tdatum \u003d bcx.p;\n+\n+\tprep_box(\u0026traf, \u0022traf\u0022, sizeof(traf));\n+\twrite_box(\u0026bcx, \u0026traf, STACK_PUSH, sizeof(traf), 0);\n+\n+\t/* moof- -\u003e tfdt (required for ffox streaming) */\n+\n+\tprep_box(\u0026tfdt.fbox.box, \u0022tfdt\u0022, sizeof(tfdt));\n+\ttfdt.base_media_decode_time\t\u003d htonl(pss-\u003edts);\n+\twrite_box(\u0026bcx, \u0026tfdt.fbox.box, STAY, sizeof(tfdt), 0);\n+\n+\t/* moof- -\u003e traf -\u003e tfhd */\n+\n+\tprep_box(\u0026tfhd_dsf.fbox.box, \u0022tfhd\u0022, sizeof(tfhd_dsf));\n+\ttfhd_dsf.fbox.flags[2]\t\t\u003d 0x20;\n+\ttfhd_dsf.track_id\t\t\u003d htonl(1);\n+//\ttfhd_dsf.default_sample_dur\t\u003d htonl(frag_dur);\n+\ttfhd_dsf.default_sample_flags\t\u003d htonl(0x01010000);\n+\twrite_box(\u0026bcx, \u0026tfhd_dsf.fbox.box, STAY, sizeof(tfhd_dsf), 0);\n+\n+\t/* moof -\u003e traf -\u003e trun\n+\t *\n+\t * data_offset; It specifies the offset from the beginning of\n+\t * the MoofBox. If only one TrunBox is specified, then the\n+\t * DataOffset field MUST be the sum of the lengths of the\n+\t * MoofBox and all the fields in the MdatBox field\n+\t *\n+\t * it means, from the start of the moof box len/\u0022moof\u0022, write here how\n+\t * many bytes to add to get to the encapsulated h.264 payload (ie, the\n+\t * annex-b stuff in there).\n+\t */\n+\n+\tprep_box(\u0026truns.fbox.box, \u0022trun\u0022, sizeof(truns));\n+\ttruns.fbox.ver\t\t\t\u003d 1;\n+\ttruns.fbox.flags[0]\t\t\u003d 0;\n+\ttruns.fbox.flags[1]\t\t\u003d 3;\n+\ttruns.fbox.flags[2]\t\t\u003d 5;\n+\ttruns.sample_count\t\t\u003d htonl(1);\n+\tofs\t\t\t\t\u003d (u32be_t *)(bcx.p +\n+\t\t\t\toffsetof(fbox_trun_subseq_t, data_offset));\n+\ttruns.first_sample_flags\t\u003d htonl(0x000305);\n+\ttruns.sample_duration\t\t\u003d htonl(frag_dur);\n+\ttruns.sample_size\t\t\u003d htonl(vlen);\n+//\ttruns.sample_composition_time_offset \u003d 0;\n+\twrite_box(\u0026bcx, \u0026truns.fbox.box, STAY, sizeof(truns), 0);\n+\n+\t/* moof -\u003e traf -\u003e uuid */\n+\n+\tprep_box(\u0026uuid_tfxd.box, \u0022uuid\u0022, sizeof(uuid_tfxd));\n+\tmemcpy(uuid_tfxd.uuid, wellknown_uuid_tfxd, sizeof(uuid_tfxd.uuid));\n+\tuuid_tfxd.version\t\t\u003d 1;\n+\tlws_ser_wu64be((uint8_t *)\u0026uuid_tfxd.frag_abs_time, pss-\u003edts);\n+\tlws_ser_wu64be((uint8_t *)\u0026uuid_tfxd.frag_dur, frag_dur);\n+\twrite_box(\u0026bcx, \u0026uuid_tfxd.box, STAY, sizeof(uuid_tfxd), 0);\n+\n+\tpss-\u003edts +\u003d frag_dur;\n+\n+\tbcx.sp \u003d 0; /* pop everything, this is a toplevel item */\n+\n+\t/* moof -\u003e mdat */\n+\n+\tprep_box(\u0026mdat, \u0022mdat\u0022, sizeof(mdat));\n+\tp \u003d bcx.p;\n+\twrite_box(\u0026bcx, \u0026mdat, 0, sizeof(mdat), vlen);\n+\tadd_u32be((u32be_t *)p, vlen);\n+\n+\tif (ofs)\n+\t\tadd_u32be(ofs, lws_ptr_diff(p + 8, datum));\n+\n+\tpss-\u003emdat_acc +\u003d lws_ptr_diff_size_t(bcx.p, start);\n+\n+//\tlwsl_hexdump_warn(start, lws_ptr_diff_size_t(bcx.p - vlen, start));\n+\n+\treturn lws_ptr_diff_size_t(bcx.p - vlen, start);\n+}\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/minimal-ws-server.c b/minimal-examples/ws-server/minimal-ws-server-v4l2/minimal-ws-server.c\nindex d88201e..bc2f5e7 100644\n--- a/minimal-examples/ws-server/minimal-ws-server-v4l2/minimal-ws-server.c\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/minimal-ws-server.c\n@@ -26,198 +26,93 @@\n #include \u003csys/mman.h\u003e\n #include \u003csys/ioctl.h\u003e\n \n-#include \u003clinux/videodev2.h\u003e\n-\n static const char *vdev \u003d \u0022/dev/video0\u0022;\n \n-/* one of these created for each message */\n-\n-struct msg {\n-\tvoid *payload; /* is malloc'd */\n-\tsize_t len;\n-};\n-\n-/* one of these is created for each client connecting to us */\n-\n-struct pss {\n-\tlws_dll2_t\t\tlist;\n-\n-\tuint8_t\t\t\tlast[500 * 1024];\n-\n-\tstruct lws\t\t*wsi;\n-\tint\t\t\tlast_sent; /* the last message number we sent */\n-\tint\t\t\tfready;\n-\tsize_t\t\t\tlast_len;\n-\tsize_t\t\t\tinside;\n-};\n-\n-struct raw_vhd {\n-\n-\tstruct lws_context\t*context;\n-\tstruct lws_vhost\t*vhost;\n-\tconst struct lws_protocols *protocol;\n-\n-\tlws_dll2_owner_t\towner;\t/* pss list */\n+#include \u0022private.h\u0022\n \n-\tstruct msg amsg; /* the one pending message... */\n-\tint current; /* the current message number we are caching */\n+// uncomment this to copy the stream to a file \u0022/tmp/str.mp4\u0022\n+#define DUMP_TO_FILE\n \n-\tstruct lws\t\t*capture_wsi;\n-\n-\tint\t\t\tfilefd;\n-\tstruct msg\t\t*buffers;\n-\tunsigned int\t\tbcount;\n-\tint\t\t\tout_buf;\n-\n-\tint\t\t\tframe;\n-};\n-\n-static int\n-xioctl(int fh, unsigned long request, void *arg)\n+static void\n+pss_to(struct lws_sorted_usec_list *sul)\n {\n-\tint r;\n+\tstruct pss *pss \u003d lws_container_of(sul, struct pss, sul);\n \n-\tdo {\n-\t\tr \u003d ioctl(fh, request, arg);\n-\t} while (-1 \u003d\u003d r \u0026\u0026 EINTR \u003d\u003d errno);\n+\tlwsl_warn(\u0022%s: timedout\u005cn\u0022, __func__);\n \n-\treturn r;\n+\tlws_wsi_close(pss-\u003ewsi, LWS_TO_KILL_ASYNC);\n }\n \n+\n static int\n-init_device(int fd)\n+__mirror_update_worst_tail(struct src_inst *si)\n {\n-\tstruct v4l2_cropcap cropcap;\n-\tstruct v4l2_queryctrl ctrl;\n-\tstruct v4l2_capability cap;\n-\tstruct v4l2_format fmt;\n-\tstruct v4l2_crop crop;\n-\tstruct v4l2_input inp;\n-\tint n \u003d 0;\n-\n-\tif (xioctl(fd, VIDIOC_S_INPUT, \u0026n) \u003d\u003d -1)\n-\t\treturn 1;\n-\n-\tif (xioctl(fd, VIDIOC_QUERYCAP, \u0026cap)) {\n-\t\tlwsl_err(\u0022%s: QUERYCAP failed\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\tlwsl_notice(\u0022cap 0x%x, bus_info %s, driver %s, card %s\u005cn\u0022,\n-\t\t(int)cap.capabilities, cap.bus_info, cap.driver, cap.card);\n-\n-\tif (!(cap.capabilities \u0026 V4L2_CAP_VIDEO_CAPTURE)) {\n-\t\tlwsl_err(\u0022%s: Device not capable of capture\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\tif (!(cap.capabilities \u0026 V4L2_CAP_STREAMING)) {\n-\t\tlwsl_err(\u0022%s: Device not capable of streaming\u005cn\u0022, __func__);\n-\n-\t\treturn 1;\n-\t}\n-\n-\t/* Select video input, video standard and tune here. */\n-\n-\tmemset(\u0026inp, 0, sizeof(inp));\n-\tif (xioctl(fd, VIDIOC_ENUMINPUT, \u0026inp) !\u003d -1)\n-\t\tlwsl_notice(\u0022%d %s %d\u005cn\u0022, inp.index, inp.name, inp.type);\n-\n-\tmemset(\u0026cropcap, 0, sizeof(cropcap));\n-\tcropcap.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\n-\tif (!xioctl(fd, VIDIOC_CROPCAP, \u0026cropcap)) {\n-\t\tcrop.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\t\tcrop.c \u003d cropcap.defrect; /* reset to default */\n-\n-\t\txioctl(fd, VIDIOC_S_CROP, \u0026crop);\n-\t}\n-\n-\tmemset(\u0026fmt, 0, sizeof(fmt));\n+\tuint32_t wai, worst \u003d 0, worst_tail \u003d 0, oldest;\n+\tstruct pss *worst_pss \u003d NULL;\n+\tstruct raw_vhd *vhd \u003d lws_container_of(si-\u003elist.owner,\n+\t\t\t\t\t struct raw_vhd, owner);\n+\n+\toldest \u003d lws_ring_get_oldest_tail(si-\u003ering);\n+\n+\tlws_start_foreach_dll(struct lws_dll2 *, d, si-\u003eowner.head) {\n+\t\tstruct pss *ps \u003d lws_container_of(d, struct pss, list);\n+\n+\t\twai \u003d (uint32_t)lws_ring_get_count_waiting_elements(si-\u003ering,\n+\t\t\t\t\t\t\t\t \u0026ps-\u003etail);\n+\t\tif (wai \u003e\u003d worst) {\n+\t\t\tworst \u003d wai;\n+\t\t\tworst_tail \u003d ps-\u003etail;\n+\t\t\tworst_pss \u003d ps;\n+\t\t}\n+\t} lws_end_foreach_dll(d);\n \n-\tfmt.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\tfmt.fmt.pix.width \u003d 1280;\n-\tfmt.fmt.pix.height \u003d 720;\n-\tfmt.fmt.pix.pixelformat \u003d V4L2_PIX_FMT_JPEG; // V4L2_PIX_FMT_H264;\n-\tfmt.fmt.pix.field \u003d V4L2_FIELD_ANY;\n+\tif (!worst_pss)\n+\t\treturn 0;\n \n-\tif (xioctl(fd, VIDIOC_S_FMT, \u0026fmt) \u003c 0) {\n-\t\tlwsl_err(\u0022%s: Can't set FMT\u005cn\u0022, __func__);\n+\tlws_ring_update_oldest_tail(si-\u003ering, worst_tail);\n+\tif (oldest \u003d\u003d lws_ring_get_oldest_tail(si-\u003ering))\n+\t\treturn 0;\n \n+\t/* if nothing in queue, no timeout needed */\n+\tif (!worst)\n \t\treturn 1;\n-\t}\n \n-\t/* Preserve original settings as set by v4l2-ctl for example */\n-\t// if (-1 \u003d\u003d xioctl(fd, VIDIOC_G_FMT, \u0026fmt))\n-\t// errno_exit(\u0022VIDIOC_G_FMT\u0022);\n-\n-\tlwsl_notice(\u0022%d %d %d %d %d\u005cn\u0022, fmt.type, fmt.fmt.pix.width,\n-\t\tfmt.fmt.pix.height, fmt.fmt.pix.field, fmt.fmt.pix.sizeimage);\n-#if 0\n-\t/* Buggy driver paranoia. */\n-\tmin \u003d fmt.fmt.pix.width * 2;\n-\tif (fmt.fmt.pix.bytesperline \u003c min)\n-\t\tfmt.fmt.pix.bytesperline \u003d min;\n-\tmin \u003d fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;\n-\tif (fmt.fmt.pix.sizeimage \u003c min)\n-\t\tfmt.fmt.pix.sizeimage \u003d min;\n-#endif\n-\n-\t/* Try the extended control API first */\n-\n-\tctrl.id \u003d V4L2_CTRL_FLAG_NEXT_CTRL;\n-\tif (!xioctl (fd, VIDIOC_QUERYCTRL, \u0026ctrl)) {\n-\t\tdo {\n-\t\t\tfprintf(stderr, \u0022%s\u005cn\u0022, ctrl.name);\n-\t\t\t//\t\tmw-\u003eadd_control(ctrl, fd, grid, gridLayout);\n-\t\t\tctrl.id |\u003d V4L2_CTRL_FLAG_NEXT_CTRL;\n-\t\t} while (!xioctl (fd, VIDIOC_QUERYCTRL, \u0026ctrl));\n-\t}\n-\n-\treturn 0;\n+\t/*\n+\t * The guy(s) with the oldest tail block the ringbuffer from recycling\n+\t * the FIFO entries he has not read yet. Don't allow those guys to\n+\t * block the FIFO operation for very long.\n+\t */\n+\tlws_start_foreach_dll(struct lws_dll2 *, d, si-\u003eowner.head) {\n+\t\tstruct pss *ps \u003d lws_container_of(d, struct pss, list);\n+\n+\t\tif (ps-\u003etail \u003d\u003d worst_tail)\n+\t\t\t/*\n+\t\t\t * Our policy is if you are the slowest connection,\n+\t\t\t * you had better take something to help with that\n+\t\t\t * within 3s, or we will hang up on you to stop you\n+\t\t\t * blocking the FIFO for everyone else.\n+\t\t\t */\n+\t\t\tlws_sul_schedule(vhd-\u003econtext, 0, \u0026ps-\u003esul,\n+\t\t\t\t\t pss_to, 3 * LWS_US_PER_SEC);\n+\t} lws_end_foreach_dll(d);\n+\n+\treturn 1;\n }\n \n-static int\n-stop_capturing(struct raw_vhd *vhd)\n+void\n+__mirror_destroy_message(void *_msg)\n {\n-\tenum v4l2_buf_type type;\n-\n-\tlws_rx_flow_control(vhd-\u003ecapture_wsi, 0);\n-\n-\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n-\ttype \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\tstruct msg *pmsg \u003d _msg;\n \n-\treturn xioctl(vhd-\u003efilefd, VIDIOC_STREAMOFF, \u0026type) \u003d\u003d -1;\n+\tfree(pmsg-\u003epayload);\n+\tpmsg-\u003epayload \u003d NULL;\n+\tpmsg-\u003elen \u003d 0;\n }\n \n-static int\n-start_capturing(struct raw_vhd *vhd)\n-{\n-\tunsigned int i;\n-\tenum v4l2_buf_type type;\n-\n-\tfor (i \u003d 0; i \u003c vhd-\u003ebcount; ++i) {\n-\t\tstruct v4l2_buffer buf;\n-\n-\t\tmemset(\u0026buf, 0, sizeof(buf));\n-\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n-\t\tbuf.index \u003d i;\n-\n-\t\tif (xioctl(vhd-\u003efilefd, VIDIOC_QBUF, \u0026buf) \u003c 0) {\n-\t\t\tlwsl_warn(\u0022%s: unable to start cap\u005cn\u0022, __func__);\n-\t\t\treturn -1;\n-\t\t}\n-\t}\n-\tlwsl_notice(\u0022%s: stream on\u005cn\u0022, __func__);\n-\n-\tlws_rx_flow_control(vhd-\u003ecapture_wsi, 1);\n+#if defined(DUMP_TO_FILE)\n+int qfd \u003d -1;\n+#endif\n \n-\ttype \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\treturn xioctl(vhd-\u003efilefd, VIDIOC_STREAMON, \u0026type) \u003d\u003d -1;\n-}\n \n /* v4l2 frame capture */\n \n@@ -228,12 +123,14 @@ callback_v4l2(struct lws *wsi, enum lws_callback_reasons reason, void *user,\n \tstruct raw_vhd *vhd \u003d (struct raw_vhd *)lws_protocol_vh_priv_get(\n \t\t\tlws_get_vhost(wsi), lws_get_protocol(wsi));\n \tstruct pss *pss \u003d (struct pss *)user;\n-\tstruct v4l2_requestbuffers req;\n+\tstruct src_inst *si \u003d NULL;\n \tlws_sock_file_fd_type u;\n \tstruct v4l2_buffer buf;\n-\t//uint8_t buf[1024];\n+\tstruct msg msg, *pmsg;\n+\tchar name[300], t[1400 + LWS_PRE];\n+\tconst char *pn;\n \tsize_t chunk;\n-\tint n;\n+\tint n, fd;\n \n \tswitch (reason) {\n \tcase LWS_CALLBACK_PROTOCOL_INIT:\n@@ -244,13 +141,15 @@ callback_v4l2(struct lws *wsi, enum lws_callback_reasons reason, void *user,\n \t\tvhd-\u003eprotocol \u003d lws_get_protocol(wsi);\n \t\tvhd-\u003evhost \u003d lws_get_vhost(wsi);\n \n-\t\tvhd-\u003efilefd \u003d open(vdev, O_RDWR | O_NONBLOCK, 0);\n-\t\tif (vhd-\u003efilefd \u003d\u003d -1) {\n+\t\tlws_pthread_mutex_init(\u0026vhd-\u003elock);\n+\n+\t\tfd \u003d open(vdev, O_RDWR | O_NONBLOCK, 0);\n+\t\tif (fd \u003d\u003d -1) {\n \t\t\tlwsl_err(\u0022Unable to open v4l2 device %s\u005cn\u0022, vdev);\n \n \t\t\treturn 1;\n \t\t}\n-\t\tu.filefd \u003d (lws_filefd_type)(long long)vhd-\u003efilefd;\n+\t\tu.filefd \u003d (lws_filefd_type)(long long)fd;\n \t\tif (!lws_adopt_descriptor_vhost(lws_get_vhost(wsi),\n \t\t\t\tLWS_ADOPT_RAW_FILE_DESC, u,\n \t\t\t\t\u0022lws-v4l2\u0022, NULL)) {\n@@ -264,17 +163,10 @@ callback_v4l2(struct lws *wsi, enum lws_callback_reasons reason, void *user,\n \t\treturn 0;\n \n bail:\n-\t\tclose(vhd-\u003efilefd);\n-\t\tvhd-\u003efilefd \u003d -1;\n+\t\tclose(fd);\n \n \t\treturn -1;\n \n-\tcase LWS_CALLBACK_PROTOCOL_DESTROY:\n-\n-\t\tif (vhd \u0026\u0026 vhd-\u003efilefd !\u003d -1)\n-\t\t\tclose(vhd-\u003efilefd);\n-\t\tbreak;\n-\n \t\t/*\n \t\t * callbacks related to capture file (no pss)\n \t\t */\n@@ -282,138 +174,97 @@ bail:\n \tcase LWS_CALLBACK_RAW_ADOPT_FILE:\n \t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_ADOPT_FILE\u005cn\u0022);\n \n-\t\tif (init_device(vhd-\u003efilefd)) {\n-\t\t\tlwsl_err(\u0022%s: device init failed\u005cn\u0022, __func__);\n-\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tvhd-\u003ecapture_wsi \u003d wsi;\n-\n-\t\tmemset(\u0026req, 0, sizeof(req));\n-\n-\t\treq.count \u003d 4;\n-\t\treq.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\t\treq.memory \u003d V4L2_MEMORY_MMAP;\n-\n-\t\tif (xioctl(vhd-\u003efilefd, VIDIOC_REQBUFS, \u0026req) \u003c 0) {\n-\t\t\tlwsl_err(\u0022%s: mmap req bufs failed\u005cn\u0022, __func__);\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tif (req.count \u003c 2) {\n-\t\t\tlwsl_err(\u0022%s: Insufficient buffer memory\u005cn\u0022, __func__);\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tvhd-\u003ebuffers \u003d calloc(req.count, sizeof(*vhd-\u003ebuffers));\n-\n-\t\tif (!vhd-\u003ebuffers) {\n-\t\t\tlwsl_err(\u0022%s: OOM\u005cn\u0022, __func__);\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tfor (vhd-\u003ebcount \u003d 0; vhd-\u003ebcount \u003c req.count; vhd-\u003ebcount++) {\n-\t\t\tstruct v4l2_buffer buf;\n-\n-\t\t\tmemset(\u0026buf, 0, sizeof(buf));\n-\n-\t\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\t\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n-\t\t\tbuf.index \u003d vhd-\u003ebcount;\n-\n-\t\t\tif (xioctl(vhd-\u003efilefd, VIDIOC_QUERYBUF, \u0026buf) \u003c 0) {\n-\t\t\t\tlwsl_err(\u0022%s: querybuf failed\u005cn\u0022, __func__);\n-\n-\t\t\t\tgoto bail1;\n-\t\t\t}\n-\n-\t\t\tvhd-\u003ebuffers[vhd-\u003ebcount].len \u003d buf.length;\n-\t\t\tvhd-\u003ebuffers[vhd-\u003ebcount].payload \u003d (void *)\n-\t\t\t mmap(NULL /* start anywhere */,\n-\t\t\t\t\tbuf.length,\n-\t\t\t\t\tPROT_READ | PROT_WRITE /* required */,\n-\t\t\t\t\tMAP_SHARED /* recommended */,\n-\t\t\t\t\tvhd-\u003efilefd, buf.m.offset);\n-\n-\t\t\tif (vhd-\u003ebuffers[vhd-\u003ebcount].payload \u003d\u003d MAP_FAILED) {\n-\t\t\t\tlwsl_err(\u0022%s: map failed\u005cn\u0022, __func__);\n-\t\t\t\tgoto bail1;\n-\t\t\t}\n-\t\t}\n+\t\tif (create_si(vhd, wsi))\n+\t\t\treturn -1;\n \n \t\tlws_rx_flow_control(wsi, 0);\n \n \t\tlwsl_notice(\u0022%s: adopt completed ok\u005cn\u0022, __func__);\n \t\tbreak;\n \n-bail1:\n-\t\tfree(vhd-\u003ebuffers);\n-\t\tvhd-\u003ebuffers \u003d NULL;\n-\n-\t\treturn 1;\n-\n \tcase LWS_CALLBACK_RAW_CLOSE_FILE:\n \t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_CLOSE_FILE\u005cn\u0022);\n \n \t\tif (!vhd)\n \t\t\tbreak;\n \n-\t\tfor (n \u003d 0; n \u003c (int)vhd-\u003ebcount; n++)\n-\t\t\tmunmap(vhd-\u003ebuffers[n].payload, vhd-\u003ebuffers[n].len);\n+\t\tlws_pthread_mutex_lock(\u0026vhd-\u003elock); /* vhost lock { */\n+\t\tsi \u003d (struct src_inst *)lws_get_opaque_user_data(wsi);\n+\n+\t\tdeinit_device(si);\n+\n+\t\tlws_pthread_mutex_unlock(\u0026vhd-\u003elock); /* } vhost lock */\n \n-\t\tfree(vhd-\u003ebuffers);\n \t\tbreak;\n \n \tcase LWS_CALLBACK_RAW_RX_FILE:\n \n-\t\tmemset(\u0026buf, 0, sizeof(buf));\n+\t\tif (!vhd)\n+\t\t\tbreak;\n \n-\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n-\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n+\t\tsi \u003d (struct src_inst *)lws_get_opaque_user_data(wsi);\n \n-\t\tif (xioctl(vhd-\u003efilefd, VIDIOC_DQBUF, \u0026buf) \u003c 0) {\n-\t\t\tif (errno \u003d\u003d EAGAIN)\n-\t\t\t\treturn 0;\n-\t\t\tlwsl_warn(\u0022%s: DQBUF ioctl fail: %d\u005cn\u0022,\n-\t\t\t\t\t__func__, errno);\n-\t\t}\n+\t\tif (lws_v4l2_dq(si, \u0026buf))\n+\t\t\treturn -1;\n \n-\t\tassert(buf.index \u003c vhd-\u003ebcount);\n+\t\tif (si-\u003eavcpc_d) {\n+\t\t\t/*\n+\t\t\t * Transcoding needed\n+\t\t\t *\n+\t\t\t * decode MJPEG frame to bitmap\n+\t\t\t */\n+\n+\t\t\tn \u003d av_parser_parse2(si-\u003eavcpc_d, si-\u003eavcc_d,\n+\t\t\t\t\t \u0026si-\u003eavp_d-\u003edata,\n+\t\t\t\t\t \u0026si-\u003eavp_d-\u003esize,\n+\t\t\t\t\t si-\u003ebuffers[buf.index].payload,\n+\t\t\t\t\t buf.bytesused, AV_NOPTS_VALUE,\n+\t\t\t\t\t AV_NOPTS_VALUE, 0);\n+\t\t\tif (n \u003c 0) {\n+\t\t\t\tlwsl_warn(\u0022%s: Parsing failed\u005cn\u0022, __func__);\n \n-\t\tvhd-\u003eframe++;\n+\t\t\t\treturn 0;\n+\t\t\t}\n \n-\t\tlws_start_foreach_dll(struct lws_dll2 *, d, vhd-\u003eowner.head) {\n-\t\t\tstruct pss *ps \u003d lws_container_of(d, struct pss, list);\n+\t\t\tif (si-\u003eavp_d-\u003esize)\n+\t\t\t\t/* encode the decoded bitmap into the\n+\t\t\t\t * output stream */\n+\t\t\t\ttranscode(si);\n+\n+\t\t} else {\n+\t\t\tmsg.payload \u003d malloc(LWS_PRE + buf.bytesused);\n+\t\t\tmsg.len \u003d buf.bytesused;\n \n-\t\t\tif (!ps-\u003einside) {\n-\t\t\t\tps-\u003elast_len \u003d buf.bytesused;\n-\t\t\t\tif (ps-\u003elast_len \u003e sizeof(ps-\u003elast) - LWS_PRE)\n-\t\t\t\t\tps-\u003elast_len \u003d sizeof(ps-\u003elast) - LWS_PRE;\n+\t\t\tif (!msg.payload)\n+\t\t\t\tlwsl_warn(\u0022%s: OOM underrun\u005cn\u0022, __func__);\n+\t\t\telse {\n \n-\t\t\t\tmemcpy(ps-\u003elast + LWS_PRE,\n-\t\t\t\t vhd-\u003ebuffers[buf.index].payload,\n-\t\t\t\t ps-\u003elast_len);\n+\t\t\t\tmemcpy(msg.payload + LWS_PRE,\n+\t\t\t\t si-\u003ebuffers[buf.index].payload,\n+\t\t\t\t msg.len);\n \n-\t\t\t\tps-\u003efready \u003d vhd-\u003eframe;\n+\t\t\t\tif (!lws_ring_insert(si-\u003ering, \u0026msg, 1)) {\n+\t\t\t\t\t__mirror_destroy_message(\u0026msg);\n+\t\t\t\t\tlwsl_notice(\u0022dropping!\u005cn\u0022);\n+\t\t\t\t}\n \t\t\t}\n \n+\t\t\tsi-\u003eframe++;\n+\t\t}\n+\n+\t\tlws_pthread_mutex_lock(\u0026si-\u003elock); /* si lock { */\n+\n+\t\tlws_start_foreach_dll(struct lws_dll2 *, d, si-\u003eowner.head) {\n+\t\t\tstruct pss *ps \u003d lws_container_of(d, struct pss, list);\n+\n \t\t\tlws_callback_on_writable(ps-\u003ewsi);\n \t\t} lws_end_foreach_dll(d);\n \n-\t\tif (xioctl(vhd-\u003efilefd, VIDIOC_QBUF, \u0026buf) \u003c 0) {\n-\t\t\tlwsl_err(\u0022%s: QBUF failed\u005cn\u0022, __func__);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tlws_pthread_mutex_unlock(\u0026si-\u003elock); /* } si lock */\n \n-\t\tbreak;\n+\t\tif (lws_v4l2_q(si, \u0026buf))\n+\t\t\treturn -1;\n \n-\tcase LWS_CALLBACK_RAW_WRITEABLE_FILE:\n-\t\tlwsl_notice(\u0022LWS_CALLBACK_RAW_WRITEABLE_FILE\u005cn\u0022);\n-\t\t/*\n-\t\t * you can call lws_callback_on_writable() on a raw file wsi as\n-\t\t * usual, and then write directly into the raw filefd here.\n-\t\t */\n \t\tbreak;\n \n \t/*\n@@ -421,58 +272,224 @@ bail1:\n \t */\n \n \tcase LWS_CALLBACK_ESTABLISHED:\n+\n+\t\tif (!vhd-\u003eowner.head)\n+\t\t\treturn 0;\n+\n \t\tpss-\u003ewsi \u003d wsi;\n \n-\t\t/* add ourselves to the list of live pss held in the vhd */\n-\t\tlws_dll2_add_tail(\u0026pss-\u003elist, \u0026vhd-\u003eowner);\n+#if defined(DUMP_TO_FILE)\n+\t\tif (qfd !\u003d -1)\n+\t\t\tclose(qfd);\n+\t\tqfd \u003d open(\u0022/tmp/str.mp4\u0022, O_CREAT|O_TRUNC|O_WRONLY, 0600);\n+#endif\n+\n+\t\tname[0] \u003d '\u005c0';\n+\t\tpn \u003d \u00220\u0022;\n+\t\tif (!lws_get_urlarg_by_name(wsi, \u0022src\u0022, name, sizeof(name) - 1))\n+\t\t\tlwsl_debug(\u0022get urlarg failed\u005cn\u0022);\n+\n+\t\t/* is there a source instance of this name already extant? */\n+\n+\t\tlws_pthread_mutex_lock(\u0026vhd-\u003elock); /* vhost lock { */\n+\n+\t\tsi \u003d NULL;\n+\t\tlws_start_foreach_dll(struct lws_dll2 *, d, vhd-\u003eowner.head) {\n+\t\t\tstruct src_inst *mi1 \u003d lws_container_of(d,\n+\t\t\t\t\t\tstruct src_inst, list);\n+\n+\t\t\tif (!strcmp(pn, mi1-\u003ename)) {\n+\t\t\t\t/* yes... we will join it */\n+\t\t\t\tsi \u003d mi1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t} lws_end_foreach_dll(d);\n+\n+\t\t/* no existing source instance for name, join first si */\n+\t\tif (!si)\n+\t\t\tsi \u003d lws_container_of(vhd-\u003eowner.head,\n+\t\t\t\t\t struct src_inst,\n+\t\t\t\t\t owner);\n+\n+\t\t/* add ourselves to the list of live pss held in the si */\n+\t\tlws_dll2_add_tail(\u0026pss-\u003elist, \u0026si-\u003eowner);\n+\n+\t\tpss-\u003etail \u003d lws_ring_get_oldest_tail(si-\u003ering);\n+\n+\t\tlws_pthread_mutex_unlock(\u0026vhd-\u003elock); /* } vh lock */\n \t\tlws_callback_on_writable(wsi);\n \n-\t\tif (vhd-\u003eowner.count \u003d\u003d 1)\n-\t\t\tstart_capturing(vhd);\n+\t\tif (si-\u003eowner.count \u003d\u003d 1)\n+\t\t\tstart_capturing(si);\n \t\tbreak;\n \n \tcase LWS_CALLBACK_CLOSED:\n+\n+\t\tif (!pss-\u003elist.owner)\n+\t\t\tbreak;\n+\n+\t\tlws_sul_cancel(\u0026pss-\u003esul);\n+\n+\t\tsi \u003d lws_container_of(pss-\u003elist.owner,\n+\t\t\t\t struct src_inst, owner);\n+\n \t\t/* remove our closing pss from the list of live pss */\n \t\tlws_dll2_remove(\u0026pss-\u003elist);\n \n-\t\tif (vhd-\u003eowner.count \u003d\u003d 0)\n-\t\t\tstop_capturing(vhd);\n+\t\tif (si-\u003eowner.count \u003d\u003d 0)\n+\t\t\tstop_capturing(si);\n+\n+#if defined(DUMP_TO_FILE)\n+\t\tif (qfd !\u003d -1)\n+\t\t\tclose(qfd);\n+#endif\n \t\tbreak;\n \n \tcase LWS_CALLBACK_SERVER_WRITEABLE:\n \n-\t\tif (!pss-\u003einside \u0026\u0026 pss-\u003elast_sent \u003d\u003d pss-\u003efready)\n+\t\tif (!pss-\u003elist.owner)\n \t\t\tbreak;\n \n-\t\tif (!pss-\u003einside)\n-\t\t\tpss-\u003elast_sent \u003d pss-\u003efready;\n+\t\tsi \u003d lws_container_of(pss-\u003elist.owner, struct src_inst, owner);\n \n-\t\t/*\n-\t\t * To work with ws-over-h2, we must restrict the size of\n-\t\t * the encapsulated frames we are sending. It's a good idea\n-\t\t * to handle h1 ws the same way in user code.\n-\t\t */\n+\t\tif (!pss-\u003einserted_mp4) {\n+\t\t\tif (!si-\u003emp4_header_len || !si-\u003esubsequent)\n+\t\t\t\treturn 0;\n \n-\t\tchunk \u003d pss-\u003elast_len - pss-\u003einside;\n-\t\tif (chunk \u003e 1400)\n-\t\t\tchunk \u003d 1400;\n+#if defined(DUMP_TO_FILE)\n+\t\t\twrite(qfd, si-\u003emp4_hdr + LWS_PRE, si-\u003emp4_header_len);\n+#endif\n \n-\t\t/* notice we allowed for LWS_PRE in the payload already */\n-\t\tn \u003d lws_write(wsi, pss-\u003elast + LWS_PRE + pss-\u003einside,\n-\t\t\t chunk, lws_write_ws_flags(LWS_WRITE_BINARY,\n-\t\t\t !pss-\u003einside, pss-\u003einside + chunk \u003d\u003d pss-\u003elast_len));\n-\t\tif (n \u003c (int)chunk) {\n-\t\t\tlwsl_err(\u0022ERROR %d writing to ws\u005cn\u0022, n);\n-\t\t\treturn -1;\n+\t\t\tlwsl_err(\u0022%s: writing %d\u005cn\u0022, __func__, (int)si-\u003emp4_header_len);\n+\t\t\tlwsl_hexdump_notice(si-\u003emp4_hdr + LWS_PRE, si-\u003emp4_header_len);\n+\t\t\tn \u003d lws_write(wsi, si-\u003emp4_hdr + LWS_PRE,\n+\t\t\t\t si-\u003emp4_header_len, LWS_WRITE_BINARY);\n+\t\t\tif (n \u003c (int)si-\u003emp4_header_len) {\n+\t\t\t\tlwsl_err(\u0022ERROR %d writing to ws\u005cn\u0022, n);\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tpss-\u003einserted_mp4 \u003d 1;\n+\t\t\tlws_callback_on_writable(wsi);\n+\n+\t\t\treturn 0;\n \t\t}\n \n-\t\tpss-\u003einside +\u003d chunk;\n-\t\tif (pss-\u003einside \u003d\u003d pss-\u003elast_len)\n-\t\t\t/* ready for new one */\n-\t\t\tpss-\u003einside \u003d 0;\n-\t\telse\n+\t\tlws_pthread_mutex_lock(\u0026si-\u003elock); /* instance lock { */\n+\t\tdo {\n+\t\t\tchar first \u003d 0, doing_moof \u003d 0;\n+\t\t\tuint8_t *place;\n+\n+\t\t\tpmsg \u003d (struct msg *)lws_ring_get_element(si-\u003ering, \u0026pss-\u003etail);\n+\t\t\tif (!pmsg)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * Chrome insists to see moof-\u003etraf-\u003etfdt boxes,\n+\t\t\t * these seem to be needed to be added later\n+\t\t\t * so they are relative to the time the peer\n+\t\t\t * joined the stream\n+\t\t\t */\n+\n+\t\t\t/*\n+\t\t\t * To work with ws-over-h2, we must restrict the size of\n+\t\t\t * the encapsulated frames we are sending. It's a good\n+\t\t\t * idea to handle h1 ws the same way in user code.\n+\t\t\t */\n+#ifdef OWN_MOOF\n+\t\t\tif (!pss-\u003edid_moof) {\n+\t\t\t\tint xlen \u003d !si-\u003eannex_b_len || pss-\u003esent_initial_moof ? 5 : si-\u003eannex_b_len;\n+\n+\t\t\t\tplace \u003d (uint8_t *)t + LWS_PRE;\n+\t\t\t\tchunk \u003d moof_prepare(si, place, pss,\n+\t\t\t\t\t\t sizeof(t) - LWS_PRE,\n+\t\t\t\t\t\t pmsg-\u003elen + xlen,\n+\t\t\t\t\t\t 0x51615);\n+\t\t\t\tfirst \u003d 1;\n+\t\t\t\tpss-\u003edid_moof \u003d 1;\n+\t\t\t\tdoing_moof \u003d 1;\n+#if 1\n+\t\t\t\tif (si-\u003eannex_b_len \u0026\u0026 !pss-\u003esent_initial_moof) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Let's give the whole set of\n+\t\t\t\t\t * NALs the first time we join the\n+\t\t\t\t\t * stream\n+\t\t\t\t\t */\n+\n+\t\t\t\t\tlwsl_hexdump_err(si-\u003eannex_b, si-\u003eannex_b_len);\n+\n+\t\t\t\t\tmemcpy(place + chunk, si-\u003eannex_b, si-\u003eannex_b_len);\n+\t\t\t\t\tchunk +\u003d si-\u003eannex_b_len;\n+\t\t\t\t} else {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * For the rest of the fragments, we\n+\t\t\t\t\t * just need the IDR NAL\n+\t\t\t\t\t */\n+\t\t\t\t\tplace[chunk++] \u003d 0x00;\n+\t\t\t\t\tplace[chunk++] \u003d 0x00;\n+\t\t\t\t\tplace[chunk++] \u003d 0x00;\n+\t\t\t\t\tplace[chunk++] \u003d 0x01;\n+\t\t\t\t\tplace[chunk++] \u003d 0x65;\n+\t\t\t\t}\n+#endif\n+\t\t\t\tpss-\u003esent_initial_moof \u003d 1;\n+\n+//\t\t\t\tlwsl_notice(\u0022%s: doing moof\u005cn\u0022, __func__);\n+//\t\t\t\tlwsl_hexdump_notice(place, chunk);\n+\n+\t\t\t} else\n+#endif\n+\t\t\t{\n+\t\t\t\tchunk \u003d pmsg-\u003elen - pss-\u003einside;\n+\t\t\t\tplace \u003d pmsg-\u003epayload + LWS_PRE + pss-\u003einside;\n+#ifdef OWN_MOOF\n+\t\t\t\tfirst \u003d 0;\n+#else\n+\t\t\t\tfirst \u003d !pss-\u003einside;\n+#endif\n+\t\t\t}\n+\n+\t\t\tif (chunk \u003e 1400)\n+\t\t\t\tchunk \u003d 1400;\n+\n+\t\t\tif (place !\u003d (uint8_t *)t + LWS_PRE)\n+\t\t\t\tmemcpy(t + LWS_PRE, place, chunk);\n+\n+\t\t\tn \u003d lws_write_ws_flags(LWS_WRITE_BINARY, first,\n+\t\t\t\t\t !doing_moof \u0026\u0026\n+\t\t\t\t\t pss-\u003einside + chunk \u003d\u003d pmsg-\u003elen);\n+#if defined(DUMP_TO_FILE)\n+\t\t\twrite(qfd, t + LWS_PRE, chunk);\n+#endif\n+\n+\t\t\tn \u003d lws_write(wsi, (uint8_t *)(t + LWS_PRE), chunk, n);\n+\t\t\tif (n \u003c (int)chunk) {\n+\t\t\t\tlwsl_err(\u0022ERROR %d writing to ws\u005cn\u0022, n);\n+\t\t\t\tlws_pthread_mutex_unlock(\u0026si-\u003elock); /* } instance lock */\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tif (!doing_moof)\n+\t\t\t\tpss-\u003einside +\u003d chunk;\n+\n+\t\t\tif (pss-\u003einside \u003d\u003d pmsg-\u003elen) {\n+\t\t\t\t/* ready for new one */\n+\t\t\t\tpss-\u003einside \u003d 0;\n+\t\t\t\tpss-\u003edid_moof \u003d 0;\n+//\t\t\t\tlwsl_notice(\u0022%s: sent %d\u005cn\u0022, __func__, (int)pmsg-\u003elen);\n+\t\t\t\tlws_ring_consume(si-\u003ering, \u0026pss-\u003etail, NULL, 1);\n+\t\t\t\t__mirror_update_worst_tail(si);\n+\t\t\t\tlws_sul_cancel(\u0026pss-\u003esul);\n+\t\t\t}\n+\n+\t\t} while (pmsg \u0026\u0026 !lws_send_pipe_choked(wsi));\n+\n+\t\tif (pss-\u003einside ||\n+\t\t lws_ring_get_count_waiting_elements(si-\u003ering, \u0026pss-\u003etail))\n \t\t\tlws_callback_on_writable(wsi);\n \n+\t\tlws_pthread_mutex_unlock(\u0026si-\u003elock); /* } instance lock */\n \t\tbreak;\n \n \tcase LWS_CALLBACK_RECEIVE:\n@@ -487,7 +504,7 @@ bail1:\n \n static struct lws_protocols protocols[] \u003d {\n \t{ \u0022http\u0022, lws_callback_http_dummy, 0, 0 },\n-\t{ \u0022lws-v4l2\u0022, callback_v4l2, sizeof(struct pss), 1300, 1300, NULL, 0 },\n+\t{ \u0022lws-v4l2\u0022, callback_v4l2, sizeof(struct pss), 1800, 1800, NULL, 0 },\n \t{ NULL, NULL, 0, 0 } /* terminator */\n };\n \n@@ -546,13 +563,7 @@ int main(int argc, const char **argv)\n \tstruct lws_context_creation_info info;\n \tstruct lws_context *context;\n \tconst char *p;\n-\tint n \u003d 0, logs \u003d LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE\n-\t\t\t/* for LLL_ verbosity above NOTICE to be built into lws,\n-\t\t\t * lws must have been configured and built with\n-\t\t\t * -DCMAKE_BUILD_TYPE\u003dDEBUG instead of \u003dRELEASE */\n-\t\t\t/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */\n-\t\t\t/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */\n-\t\t\t/* | LLL_DEBUG */;\n+\tint n \u003d 0, logs \u003d LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;\n \n \tsignal(SIGINT, sigint_handler);\n \n@@ -566,12 +577,12 @@ int main(int argc, const char **argv)\n \tlwsl_user(\u0022LWS minimal ws server V4L2 | visit http://localhost:7681\u005cn\u0022);\n \n \tmemset(\u0026info, 0, sizeof info); /* otherwise uninitialized garbage */\n-\tinfo.port \u003d 7681;\n-\tinfo.mounts \u003d \u0026mount;\n-\tinfo.protocols \u003d protocols;\n-\tinfo.vhost_name \u003d \u0022localhost\u0022;\n-\tinfo.options \u003d 0;\n-\tinfo.headers \u003d pvo_hsbph;\n+\tinfo.port\t\t\u003d 7681;\n+\tinfo.mounts\t\t\u003d \u0026mount;\n+\tinfo.protocols\t\t\u003d protocols;\n+\tinfo.vhost_name\t\t\u003d \u0022localhost\u0022;\n+\tinfo.options\t\t\u003d 0;\n+\tinfo.headers\t\t\u003d pvo_hsbph;\n \n #if defined(LWS_WITH_TLS)\n \tif (lws_cmdline_option(argc, argv, \u0022-s\u0022)) {\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/example.js b/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/example.js\nindex d9f8c3e..1810bfc 100644\n--- a/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/example.js\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/example.js\n@@ -1,3 +1,25 @@\n+/*\n+ * based on\n+ * https://github.com/elsampsa/websocket-mse-demo/blob/master/ws_client_new.html\n+ */\n+var uw \u003d window.URL || window.webkitURL;\n+var verbose \u003d false;\n+var buffering_sec \u003d 0; /* we want to keep this much in reserve in the local buffer */\n+ \n+var stream_started \u003d false;\n+var ms;\n+var queue \u003d [];\n+var stream_live;\n+var seeked \u003d false;\n+var cc \u003d 0;\n+var oneshot \u003d 0;\n+var erste \u003d [] , erste_ready \u003d 0;\n+ \n+var source_buffer, codecpars;\n+var pass \u003d 0;\n+\n+var buffering_sec_seek \u003d buffering_sec * 0.9; \n+var buffering_sec_seek_distance \u003d buffering_sec * 0.5; \n \n function get_appropriate_ws_url(extra_url)\n {\n@@ -25,35 +47,386 @@ function get_appropriate_ws_url(extra_url)\n \treturn pcol + u[0] + \u0022/\u0022 + extra_url;\n }\n \n+var ws;\n+\n function new_ws(urlpath, protocol)\n {\n \treturn new WebSocket(urlpath, protocol);\n }\n \n \n-var uw \u003d window.URL || window.webkitURL;\n+/*\n+ * taken from here: https://stackoverflow.com/questions/54186634/\n+ * sending-periodic-metadata-in-fragmented-live-mp4-stream/\n+ */\n+ \n+function toInt(arr, index)\n+{\n+\tvar dv \u003d new DataView(arr.buffer, 0);\n \n-document.addEventListener(\u0022DOMContentLoaded\u0022, function() {\n+\treturn dv.getInt32(index, false);\n+}\n+\n+function toString(arr, fr, to)\n+{\n+\treturn String.fromCharCode.apply(null, arr.slice(fr,to));\n+}\n+\n+function getBox(arr, i)\n+{\n+\treturn [toInt(arr, i), toString(arr, i+4, i+8)]\n+}\n+\n+function getSubBox(arr, box_name, _ofs, ifs)\n+{\n+ var i \u003d _ofs;\n+ res \u003d getBox(arr, i);\n \n-\tvar ws \u003d new_ws(get_appropriate_ws_url(\u0022\u0022), \u0022lws-v4l2\u0022);\n+ main_length \u003d res[0];\n+ name \u003d res[1]; // this boxes length and name\n+ \n+ // console.log(\u0022subb: \u0022 + name + \u0022 len \u0022 + main_length);\n \n-\tconsole.log(get_appropriate_ws_url(\u0022\u0022));\n+ i \u003d i + 8 + ifs;\n+ \n+ var sub_box \u003d null;\n+ \n+ while (i \u003c main_length + _ofs) {\n+ res \u003d getBox(arr, i);\n+ l \u003d res[0];\n+ name \u003d res[1];\n+ \n+ // console.log(\u0022 inner: \u0022 + name + \u0022 l \u0022 + main_length);\n+ \n+ if (box_name \u003d\u003d name) {\n+ sub_box \u003d arr.slice(i, i+l);\n+ return sub_box;\n+ }\n+ i \u003d i + l;\n+ }\n+ return sub_box;\n+}\n+\n+function hasFirstSampleFlag(arr, _ofs)\n+{ \n+ var traf \u003d getSubBox(arr, \u0022traf\u0022, _ofs, 0);\n+ if (traf\u003d\u003dnull) { console.log(\u0022no traf in moof\u0022); return false; }\n+ \n+ var trun \u003d getSubBox(traf, \u0022trun\u0022, 0, 0);\n+ if (trun\u003d\u003dnull) { console.log(\u0022no trun in traf\u0022); return false; }\n+ \n+ // ISO/IEC 14496-12:2012(E) .. pages 5 and 57\n+ // bytes: (size 4), (name 4), (version 1 + tr_flags 3)\n+ var flags \u003d trun.slice(9,12);\n+ \n+// console.log(flags);\n+ f \u003d flags[2] \u0026 4; // console.log(f);\n+ return f \u003d\u003d 4;\n+ }\n+ \n+function hexdump(buffer, blockSize)\n+{\t\n+\tif (typeof buffer \u003d\u003d\u003d 'string') {\n+\t} else if (buffer instanceof ArrayBuffer \u0026\u0026 buffer.byteLength !\u003d\u003d undefined) {\n+\t\tbuffer \u003d String.fromCharCode.apply(String,\n+\t\t\t\t[].slice.call(new Uint8Array(buffer)));\n+\t} else if (Array.isArray(buffer)){\n+\t\tbuffer \u003d String.fromCharCode.apply(String, buffer);\n+\t} else if (buffer.constructor \u003d\u003d\u003d Uint8Array) {\n+\t\tbuffer \u003d String.fromCharCode.apply(String,\n+\t\t\t\t[].slice.call(buffer));\n+\t} else {\n+\t\tconsole.log(\u0022Error: buffer is unknown...\u0022);\n+\t\treturn false;\n+\t}\t\n+ \n+\tblockSize \u003d blockSize || 16;\n+ var lines \u003d [];\n+ var hex \u003d \u00220123456789ABCDEF\u0022;\n+ for (var b \u003d 0; b \u003c buffer.length; b +\u003d blockSize) {\n+ var block \u003d buffer.slice(b, Math.min(b + blockSize, buffer.length));\n+ var addr \u003d (\u00220000\u0022 + b.toString(16)).slice(-4);\n+ var codes \u003d block.split('').map(function (ch) {\n+ var code \u003d ch.charCodeAt(0);\n+ return \u0022 \u0022 + hex[(0xF0 \u0026 code) \u003e\u003e 4] + hex[0x0F \u0026 code];\n+ }).join(\u0022\u0022);\n+ codes +\u003d \u0022 \u0022.repeat(blockSize - block.length);\n+ var chars \u003d block.replace(/[\u005cx00-\u005cx1F\u005cx20]/g, '.');\n+ chars +\u003d \u0022 \u0022.repeat(blockSize - block.length);\n+ lines.push(addr + \u0022 \u0022 + codes + \u0022 \u0022 + chars);\n+ }\n+ return lines.join(\u0022\u005cn\u0022);\n+}\n+\n+function dtox(d, padding)\n+{\n+\tvar hex \u003d Number(d).toString(16);\n+\t\n+\tpadding \u003d typeof(padding) \u003d\u003d\u003d \u0022undefined\u0022 ||\n+\t\t\tpadding \u003d\u003d\u003d null ? padding \u003d 2 : padding;\n+\n+\twhile (hex.length \u003c padding)\n+ \thex \u003d \u00220\u0022 + hex;\n+\n+\treturn hex;\n+}\n+\n+function ms_opened()\n+{\n+\tif (oneshot)\n+\t\treturn;\n+\n+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/duration\n+ console.log(\u0022mediasource.opened()\u0022);\n+ stream_live \u003d document.getElementById('stream_live');\n+ URL.revokeObjectURL(stream_live.src);\n+ \n+ oneshot \u003d 1;\n+ ms.duration \u003d buffering_sec;\n+\n+ source_buffer \u003d ms.addSourceBuffer(codecPars);\n+ source_buffer.mode \u003d 'sequence';\n+\t\n+\tconsole.log(ms.readyState);\n+ \n+ source_buffer.addEventListener(\u0022updateend\u0022, loadPacket);\n+ if (queue.length) {\n+ \tconsole.log(\u0022ms_opened: loading queued pkt\u0022);\n+ loadPacket();\n+ }\n+}\n+ \n+function ms_closed() {\n+\tconsole.log(\u0022mediasource closed()\u0022);\n+\tws.close();\n+}\n+ \n+function ms_ended() {\n+\tconsole.log(\u0022mediasource ended()\u0022);\n+\tws.close();\n+}\n+\n+var _appendBuffer \u003d function(buffer1, buffer2) {\n+ var tmp \u003d new Uint8Array(buffer1.byteLength + buffer2.byteLength);\n+ tmp.set(new Uint8Array(buffer1), 0);\n+ tmp.set(new Uint8Array(buffer2), buffer1.byteLength);\n+ return tmp.buffer;\n+};\n+\n+var ofs \u003d 0;\n+ \n+function putPacket(arr)\n+{ \n+\tvar memview \u003d new Uint8Array(arr);\n+\tvar sanity \u003d 50;\n+ \n+\t// console.log(\u0022putPacket: \u0022 + memview.byteLength);\n+ // console.log(hexdump(arr, 16));\n+\n+\tofs \u003d 0;\n+\tif (pass \u003c 3) {\n+\t\n+//\t console.log(hexdump(arr, 16));\n+\n+\t\twhile (sanity-- \u0026\u0026 pass \u003c 3 \u0026\u0026 ofs \u003c memview.byteLength) {\n+\t\n+\t\t res \u003d getBox(memview, ofs);\n+\t\t main_length \u003d res[0];\n+\t\t name \u003d res[1];\n+\t\t \n+\t\t //console.log(\u0022box \u0022 + name + \u0022, len \u0022 + main_length);\n+\n+\t\t\tswitch (pass) {\n+\t\t\tcase 0:\t\t \n+\t\t \tif (name !\u003d \u0022styp\u0022 \u0026\u0026 name !\u003d \u0022ftyp\u0022)\n+\t\t \t\tbreak;\n+\t\t\t\tpass++;\n+\t\t\t\tbreak;\n+\t\t case 1:\n+\t\t \tif (name !\u003d\u0022moov\u0022)\n+\t\t \t\tbreak;\n+\t\t \tpass++;\n+\t\t\t\tsb \u003d getSubBox(memview, \u0022trak\u0022, ofs, 0);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022mdia\u0022, 0, 0);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022minf\u0022, 0, 0);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022stbl\u0022, 0, 0);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022stsd\u0022, 0, 0);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022avc1\u0022, 0, 8);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\t\t\t\tsb \u003d getSubBox(sb, \u0022avcC\u0022, 0, 78);\n+\t\t\t\tif (!sb)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\t/* audio is tbd */\n+\t\t\t\t\n+\t\t\t\tcodecPars \u003d \u0022video/mp4\u0022+';codecs\u003d\u0022avc1.' +\n+\t\t\t\t\t\tdtox(sb[9]) + dtox(sb[10]) +\n+\t\t\t\t\t\tdtox(sb[11]) +'\u0022';\n+\n+\t\t\t\tif (!MediaSource.isTypeSupported(codecPars)) {\n+\t\t \tconsole.log(\u0022Mimetype \u0022 + codecPars +\n+\t\t \t\t\t\u0022 not supported\u0022);\n+\t\t \tws.close();\n+\t \t} else\n+\t \t\tconsole.log(\u0022Mimetype \u0022 + codecPars +\n+\t \t\t\t\t\u0022 supported\u0022);\n+\n+\t\t \tbreak;\n+\t\t\tcase 2:\n+\t\t \tif (name !\u003d \u0022moof\u0022)\n+\t\t \t\tbreak;\n+\t\t \tif (hasFirstSampleFlag(memview, ofs)) {\n+\t\t \tpass++;\n+\t\t\t\t\tconsole.log(\u0022moof flags OK\u0022);\n+\t\t\t\t} else\n+\t\t \treturn;\n+\t\t break;\n+\t\t\tdefault:\n+\t\t\t\tbreak;\n+\t\t }\n+\t \n+\t ofs +\u003d main_length;\n+\t\t}\n+\t}\n+\n+\tif (0 \u0026\u0026 pass \u003c 3) {\n+\t\tconsole.log(\u0022erste: adding \u0022 + arr.byteLength);\n+\t\tif (!erste_ready)\n+\t\t\terste \u003d arr;\n+\t\telse\n+\t \terste \u003d _appendBuffer(erste, arr);\n+ \terste_ready \u003d 1;\n+ \tconsole.log(\u0022done\u0022);\n+ \treturn;\n+ } \n+ \n+ if (!ms) {\n+\t\tms \u003d new MediaSource();\n+ ms.addEventListener('sourceopen', ms_opened, false);\n+ ms.addEventListener('sourceclosed', ms_closed, false);\n+ ms.addEventListener('sourceended', ms_ended, false);\n+ \n+ console.log(\u0022opening mediasource\u0022);\n+ \tstream_live \u003d document.getElementById('stream_live');\n+ \tstream_live.src \u003d window.URL.createObjectURL(ms);\n+\t}\n+ \n+ \tofs \u003d 0;\n+ \t\n+ \tif (erste_ready) {\n+ \t\tconsole.log(\u0022draining erste\u0022);\n+ \t\tarr \u003d _appendBuffer(erste, arr);\n+ \t\t// console.log(hexdump(arr, 16));\n+ \t\terste_ready \u003d 0;\n+ \t}\n+ \n+ // keep the latency to minimum\n+ \n+ \tif (stream_live) {\n+\t let latest \u003d stream_live.duration;\n+\n+\t if ((stream_live.duration \u003e\u003d buffering_sec) \u0026\u0026 \n+\t ((latest - stream_live.currentTime) \u003e buffering_sec_seek)) {\n+\t //console.log(\u0022seek from \u0022, stream_live.currentTime, \u0022 to \u0022, latest);\n+\t df \u003d (stream_live.duration - stream_live.currentTime); // this much away from the last available frame\n+\t if ((df \u003e buffering_sec_seek)) {\n+\t seek_to \u003d stream_live.duration - buffering_sec_seek_distance;\n+\t stream_live.currentTime \u003d seek_to;\n+\t }\n+\t }\n+ }\n+\n+ data \u003d arr;\n+ if (queue.length \u003d\u003d 0 \u0026\u0026 source_buffer \u0026\u0026 !source_buffer.updating) {\n+\n+\t\ttry {\n+\n+\t\t\tconsole.log(\u0022direct, len \u0022 + data.byteLength);\n+\t\t\tconsole.log(hexdump(data, 16));\n+\n+\t\t\tsource_buffer.timestampOffset \u003d ms.duration;\n+\t\t \tsource_buffer.appendBuffer(data);\n+\n+\t\t} catch(exc) {\n+\t\t\tconsole.log(\u0022exception: source_buffer.appendBuffer \u0022 + exc);\n+\t\t\tws.close();\n+\t\t \treturn;\n+\t\t};\n+ \n+ // console.log(hexdump(arr, 16));\n+\n+ //\tcc \u003d cc + 1;\n+ \treturn;\n+ }\n+ \n+ queue.push(data); // add to the end\n+ console.log(\u0022queue push:\u0022 + queue.length + \u0022, len: \u0022 + data.byteLength);\n+ }\n+ \n+ \n+ \n+function loadPacket()\n+{\t\n+\tif (!queue.length)\n+\t\treturn;\n+\t\n+\tif (!source_buffer)\n+\t\treturn;\n+\n+\tstream_started \u003d true;\n+ \n+\tinp \u003d queue.shift();\n+\t\tconsole.log(\u0022loadPacket \u0022 + ms.readyState + \u0022, dur \u0022 + stream_live.duration + \u0022, ms dur \u0022 + ms.duration + \u0022, len \u0022 + inp.byteLength);\n+\t\n+\tif (verbose) { console.log(\u0022queue pop:\u0022, queue.length); }\n+ \n+\tvar memview \u003d new Uint8Array(inp);\n+\t\n+\tif (verbose) { console.log(\u0022 \u003d\u003d\u003e writing buffer with\u0022, memview[0], memview[1], memview[2], memview[3]); }\n+\t\n+\ttry {\n+\n+//\t\tconsole.log(hexdump(inp, 16));\n+//\t\tsource_buffer.timestampOffset \u003d 0; //ms.duration;\n+\t\tsource_buffer.appendBuffer(inp);\n+\n+\t} catch(e) {\n+\t\tconsole.log(\u0022appending buf failed\u0022);\n+\t \tws.close();\n+\t \treturn;\n+\t}\n+\n+\tcc \u003d cc + 1;\n+}\n+\n+document.addEventListener(\u0022DOMContentLoaded\u0022, function() {\n+ \n+ ws \u003d new_ws(get_appropriate_ws_url(\u0022\u0022), \u0022lws-v4l2\u0022);\n \n \tws.binaryType \u003d 'arraybuffer';\n \ttry {\n \t\tws.onopen \u003d function() {\n-\n+\t\t\tofs \u003d 0;\n \t\t};\n \n \t\tws.onmessage \u003d function got_packet(msg) {\n-\t\t var image \u003d document.getElementById('img');\n-\t\t var bl \u003d new Blob([msg.data], { type: \u0022image/jpeg\u0022 });\n-\n-\t\t image.src \u003d uw.createObjectURL(bl);\n-\t\t uw.revokeObjectURL(bl);\n+\t\t\t// console.log(\u0022ws.onmessage\u0022);\n+\t\t putPacket(msg.data);\n \t\t};\n \n \t\tws.onclose \u003d function(){\n+\t\t\tconsole.log(\u0022ws closed\u0022);\n \t\t};\n \t} catch(exception) {\n \t\talert(\u0022\u003cp\u003eError \u0022 + exception);\n@@ -67,4 +440,4 @@ document.addEventListener(\u0022DOMContentLoaded\u0022, function() {\n \n \t//document.getElementById(\u0022b\u0022).addEventListener(\u0022click\u0022, sendmsg);\n \n-}, false);\n\u005c No newline at end of file\n+}, false);\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/index.html b/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/index.html\nindex ed3043f..a2ac2cd 100644\n--- a/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/index.html\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/mount-origin/index.html\n@@ -9,6 +9,15 @@\n \n \t\tLWS chat \u003cb\u003eminimal ws server VL42 example\u003c/b\u003e. \u003cbr\u003eV4L2 capture JPEG stills sent over ws as the browser can take them..\n \t\t\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n-\t\t\u003cdiv\u003e\u003cimg id\u003d\u0022img\u0022\u003e\u003c/div\u003e\n+\t\t\u003cdiv\u003e\n+\t\t \u003c!-- muted attribute required for chrome autoplay--\u003e\n+ \u003cvideo id\u003d\u0022stream_live\u0022 \n+ width\u003d\u0022640\u0022 height\u003d\u0022360\u0022 \n+ controls\u003d\u0022false\u0022 autoplay\u003d\u0022true\u0022\n+ muted\u003d\u0022muted\u0022 \n+ preload\u003d\u0022auto\u0022\u003e\n+ Your browser does not support the video tag.\n+ \u003c/video\u003e \n+\t\t\u003c/div\u003e\n \t\u003c/body\u003e\n \u003c/html\u003e\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/private.h b/minimal-examples/ws-server/minimal-ws-server-v4l2/private.h\nnew file mode 100644\nindex 0000000..6da1171\n--- /dev/null\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/private.h\n@@ -0,0 +1,166 @@\n+/* ffmpeg-devel package on Fedora */\n+#include \u003clibavcodec/avcodec.h\u003e\n+#include \u003clibavformat/avformat.h\u003e\n+#include \u003clibswscale/swscale.h\u003e\n+#include \u003clibavutil/opt.h\u003e\n+\n+#include \u003clinux/videodev2.h\u003e\n+\n+#define QUEUELEN 64\n+\n+/* comment this to use the MOOF from libav*, otherwise we generate both the\n+ * onetime mp4 headers and the individual moof headers from scratch. When\n+ * using OWN_MOOF, in fact we only use libav as a way to start the h264 encoder.\n+ */\n+#define OWN_MOOF\n+\n+typedef uint64_t u64be_t;\n+typedef uint32_t u32be_t;\n+typedef uint16_t u16be_t;\n+\n+/* one of these created for each message */\n+\n+struct msg {\n+\tvoid *payload; /* is malloc'd */\n+\tsize_t len;\n+};\n+\n+/* one of these per video source */\n+\n+struct src_inst {\n+\tlws_dll2_t\t\t\tlist;\n+\tlws_pthread_mutex(lock) /* protects all mirror instance data */\n+\tlws_dll2_owner_t\t\towner;\t/* pss list */\n+\t/**\u003c must hold the the per_vhost_data__lws_mirror.lock as well\n+\t * to change si list membership */\n+\tstruct lws_ring\t\t\t*ring;\n+\tint\t\t\t\tmessages_allocated;\n+\n+#if !defined(OWN_MOOF)\n+\tuint8_t\t\t\t\tiobuf[256 * 1024];\n+#endif\n+\tchar\t\t\t\tname[30];\n+\n+\tuint8_t\t\t\t\tpre[4096], annex_b[4096];\n+\tunsigned int\t\t\tpre_len, annex_b_len;\n+\tuint8_t\t\t\t\tchonk[256 * 1024];\n+\n+\tuint8_t\t\t\t\tmp4_hdr[2048];\n+\tunsigned int\t\t\tmp4_header_len;\n+\n+\tuint8_t\t\t\t\th264_sps[128];\n+\tunsigned int\t\t\th264_sps_len;\n+\tuint8_t\t\t\t\th264_pps[128];\n+\tunsigned int\t\t\th264_pps_len;\n+\n+\tuint8_t\t\t\t\tavcc_header[LWS_PRE + 384];\n+\tunsigned int\t\t\tavcc_header_len;\n+\n+\tuint64_t\t\t\tntt, dtt;\n+\n+\tchar\t\t\t\tab_type;\n+\tunsigned int\t\t\tab_state;\n+\tunsigned int\t\t\tab_seq;\n+\n+\tstruct lws\t\t\t*capture_wsi;\n+\tint\t\t\t\tfilefd;\n+\n+\tstruct msg\t\t\t*buffers;\n+\tunsigned int\t\t\tbcount;\n+\n+\tint\t\t\t\tframe;\n+\tint\t\t\t\twidth;\n+\tint\t\t\t\theight;\n+\n+\tint\t\t\t\tdec_frame;\n+\tint\t\t\t\tenc_frame;\n+\n+\tchar\t\t\t\tsubsequent;\n+\tchar\t\t\t\tissued;\n+\n+\tAVDictionary\t\t\t*opt;\n+\n+\tAVCodecParserContext\t\t*avcpc_d;\n+//\tstruct SwsContext\t\t*swsc;\n+\tAVCodec\t\t\t\t*avc_d;\n+\tAVCodecContext\t\t\t*avcc_d;\n+\tAVFrame\t\t\t\t*avf_d;\n+\tAVPacket\t\t\t*avp_d;\n+\n+\tAVCodec\t\t\t\t*avc_e;\n+\tAVCodecContext\t\t\t*avcc_e;\n+\tAVFormatContext\t\t\t*avfc_e;\n+\tAVPacket\t\t\t*avp_e;\n+\tAVStream\t\t\t*avs_e;\n+\tAVIOContext\t\t\t*avioc_e;\n+};\n+\n+/* one of these is created for each client connecting to us */\n+\n+struct pss {\n+\tlws_dll2_t\t\t\tlist; /* si owner */\n+\n+\tlws_sorted_usec_list_t\t\tsul;\n+\n+\tuint64_t\t\t\tmdat_acc, dts;\n+\tuint32_t\t\t\tmfhd_cnt;\n+\tuint32_t\t\t\tseq;\n+\n+\tstruct lws\t\t\t*wsi;\n+\tuint32_t\t\t\ttail;\n+\tsize_t\t\t\t\tinside;\n+\n+\tchar\t\t\t\tinserted_mp4;\n+\tchar\t\t\t\tatomic;\n+\tchar\t\t\t\tsent_initial_moof;\n+\tchar\t\t\t\tdid_moof;\n+};\n+\n+struct raw_vhd {\n+\n+\tstruct lws_context\t\t*context;\n+\tstruct lws_vhost\t\t*vhost;\n+\tconst struct lws_protocols\t*protocol;\n+\n+\tlws_pthread_mutex(lock) /* protects mi_list membership changes */\n+\n+\tlws_dll2_owner_t\t\towner;\t/* src_inst list */\n+};\n+\n+int\n+init_device(struct src_inst *si);\n+int\n+deinit_device(struct src_inst *si);\n+int\n+start_capturing(struct src_inst *si);\n+int\n+stop_capturing(struct src_inst *si);\n+\n+int\n+create_si(struct raw_vhd *vhd, struct lws *wsi);\n+\n+size_t\n+annex_b_scan(struct src_inst *si, uint8_t *data, size_t len);\n+\n+int\n+lws_v4l2_dq(struct src_inst *si, struct v4l2_buffer *buf);\n+\n+int\n+lws_v4l2_q(struct src_inst *si, struct v4l2_buffer *buf);\n+\n+void\n+add_u32be(u32be_t *dest, unsigned int add_le);\n+\n+void\n+__mirror_destroy_message(void *_msg);\n+\n+int\n+transcode(struct src_inst *si);\n+\n+int\n+mp4_header_prepare(struct src_inst *si);\n+\n+size_t\n+moof_prepare(struct src_inst *si, uint8_t *start, struct pss *pss,\n+\t size_t max, size_t vlen, unsigned int frag_dur);\n+\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/transcode.c b/minimal-examples/ws-server/minimal-ws-server-v4l2/transcode.c\nnew file mode 100644\nindex 0000000..5ad31e0\n--- /dev/null\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/transcode.c\n@@ -0,0 +1,185 @@\n+/*\n+ * lws-minimal-ws-server-v4l2\n+ *\n+ * Written in 2010-2021 by Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+\n+#include \u0022private.h\u0022\n+\n+#include \u003cassert.h\u003e\n+\n+int\n+transcode(struct src_inst *si)\n+{\n+#if defined(OWN_MOOF)\n+\tstruct msg msg;\n+#endif\n+\tint n;\n+\n+\tn \u003d avcodec_send_packet(si-\u003eavcc_d, si-\u003eavp_d);\n+\n+\tif (n \u003c 0) {\n+\t\tlwsl_warn(\u0022%s: send_pkt failed\u005cn\u0022, __func__);\n+\n+\t\treturn 0;\n+\t}\n+\n+\twhile (n \u003e\u003d 0) {\n+\t\tn \u003d avcodec_receive_frame(si-\u003eavcc_d, si-\u003eavf_d);\n+\t\tif (n \u003d\u003d AVERROR(EAGAIN) ||\n+\t\t n \u003d\u003d AVERROR_EOF)\n+\t\t\treturn 0;\n+\t\tif (n \u003c 0) {\n+\t\t\tlwsl_warn(\u0022%s: dec failed\u005cn\u0022, __func__);\n+\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\t/*\n+\t\t * si-\u003eavf_d holds a decoded bitmap... let's give it to the\n+\t\t * h.264 encoder we have prepared earlier\n+\t\t */\n+\n+\t\tsi-\u003eavf_d-\u003epts \u003d av_rescale_q((si-\u003edec_frame++) *\n+\t\t\t\t\t\tsi-\u003eavcc_e-\u003eticks_per_frame,\n+\t\t\t\t\t si-\u003eavcc_d-\u003etime_base,\n+\t\t\t\t\t (AVRational) { 1, 10000000 });\n+\n+\t\tn \u003d avcodec_send_frame(si-\u003eavcc_e, si-\u003eavf_d);\n+\n+\t\tif (n \u003c 0) {\n+\t\t\tlwsl_err(\u0022%s: send_frame failed: %s\u005cn\u0022, __func__,\n+\t\t\t\t av_err2str(n));\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\twhile (n \u003e\u003d 0) {\n+#if defined(OWN_MOOF)\n+\t\t\tsize_t trim \u003d 0;\n+#endif\n+\t\t\tuint8_t *od;\n+\t\t\tsize_t os;\n+\n+\t\t\tn \u003d avcodec_receive_packet(si-\u003eavcc_e, si-\u003eavp_e);\n+\n+\t\t\tif (n \u003d\u003d AVERROR(EAGAIN) ||\n+\t\t\t n \u003d\u003d AVERROR_EOF)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (n \u003c 0) {\n+\t\t\t\tlwsl_warn(\u0022%s: receive_pkt failed\u005cn\u0022, __func__);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * The encoder produces an annex-b TS type header.\n+\t\t\t * Let's parse it to get the SPS and PPS for later\n+\t\t\t */\n+\n+\t\t\tif (!si-\u003eavcc_header_len)\n+\t\t\t\tlwsl_hexdump_warn(si-\u003eavp_e-\u003edata, si-\u003eavp_e-\u003esize);\n+\n+\t\t\tod \u003d si-\u003eavp_e-\u003edata;\n+\t\t\tos \u003d si-\u003eavp_e-\u003esize;\n+\n+\t\t\tif (si-\u003eavp_e-\u003esize) {\n+#if defined(OWN_MOOF)\n+\t\t\t\ttrim \u003d\n+#endif\n+\t\t\t\t\tannex_b_scan(si, si-\u003eavp_e-\u003edata, si-\u003eavp_e-\u003esize);\n+\n+//\t\t\t\tlwsl_notice(\u0022%s: trim 0x%x\u005cn\u0022, __func__, (unsigned int)trim);\n+//\t\t\t\tlwsl_hexdump_notice(si-\u003eavp_e-\u003edata, trim);\n+\t\t\t\t//si-\u003eavp_e-\u003edata +\u003d trim;\n+\t\t\t\t//si-\u003eavp_e-\u003esize -\u003d trim;\n+\n+\t\t\t\tif (!si-\u003eavcc_header_len) {\n+\t\t\t\t\tassert(si-\u003epre_len + trim \u003c sizeof(si-\u003epre));\n+\t\t\t\t\tmemcpy(si-\u003epre + si-\u003epre_len, si-\u003eavp_e-\u003edata, trim);\n+\t\t\t\t\tsi-\u003epre_len +\u003d trim;\n+\t\t\t\t}\n+\n+\t\t\t\tif (!si-\u003eannex_b_len) {\n+\t\t\t\t\tassert(si-\u003eannex_b_len + trim \u003c sizeof(si-\u003eannex_b));\n+\t\t\t\t\tmemcpy(si-\u003eannex_b + si-\u003eannex_b_len, si-\u003eavp_e-\u003edata, trim);\n+\t\t\t\t\tsi-\u003eannex_b_len \u003d trim;\n+\t\t\t\t}\n+#if defined(OWN_MOOF)\n+\t\t\t\telse\n+\t\t\t\t\tsi-\u003esubsequent \u003d 1;\n+#endif\n+\n+\t\t\t\t//trim \u003d 0;\n+\t\t\t}\n+\n+#if defined(OWN_MOOF)\n+\t\t\tmsg.len \u003d si-\u003eavp_e-\u003esize - trim;\n+\t\t\tmsg.payload \u003d malloc(LWS_PRE + msg.len);\n+\t\t\tif (!msg.payload) {\n+\t\t\t\tlwsl_warn(\u0022%s: OOM underrun\u005cn\u0022, __func__);\n+\t\t\t\tav_packet_unref(si-\u003eavp_e);\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\n+\t\t\tmemcpy(msg.payload + LWS_PRE,\n+\t\t\t\t si-\u003eavp_e-\u003edata + trim, msg.len);\n+\n+\t\t\tif (!lws_ring_insert(si-\u003ering, \u0026msg, 1)) {\n+\t\t\t\t__mirror_destroy_message(\u0026msg);\n+\t\t\t\tlwsl_notice(\u0022dropping!\u005cn\u0022);\n+\t\t\t}\n+\n+#else\n+\n+\t\t\tif (si-\u003eavp_e-\u003esize \u0026\u0026\n+\t\t\t /* don't start emitting until we had the annex B avcc decode */\n+\t\t\t si-\u003eavcc_header_len) {\n+\n+\t\t\t\tif (!si-\u003esubsequent) {\n+\t\t\t\t\tif (avformat_write_header(si-\u003eavfc_e, NULL))\n+\t\t\t\t\t\tlwsl_err(\u0022%s: couldn't write header\u005cn\u0022, __func__);\n+\t\t\t\t\tsi-\u003esubsequent \u003d 1;\n+\t\t\t\t}\n+\n+\t\t\t\tif (si-\u003epre_len) {\n+\n+\t\t\t\t\t/*\n+\t\t\t\t\t * If we're using libav and there was\n+\t\t\t\t\t * a packet without an IDR, we have to\n+\t\t\t\t\t * make new packet bodies for the IDRs\n+\t\t\t\t\t * with the pre NALs prepended\n+\t\t\t\t\t */\n+\n+\t\t\t\t\tmemcpy(si-\u003echonk, si-\u003epre, si-\u003epre_len);\n+\t\t\t\t\tmemcpy(si-\u003echonk + si-\u003epre_len,\n+\t\t\t\t\t si-\u003eavp_e-\u003edata, si-\u003eavp_e-\u003esize);\n+\t\t\t\t\tsi-\u003eavp_e-\u003edata \u003d si-\u003echonk;\n+\t\t\t\t\tsi-\u003eavp_e-\u003esize +\u003d si-\u003epre_len;\n+\t\t\t\t}\n+\n+\t\t\t\tav_packet_rescale_ts(si-\u003eavp_e,\n+\t\t\t\t\t\t si-\u003eavcc_d-\u003etime_base,\n+\t\t\t\t\t\t si-\u003eavcc_e-\u003etime_base);\n+\n+\t\t\t\tsi-\u003entt \u003d (uint64_t)si-\u003eavp_e-\u003edts;\n+\n+\t\t\t\tif (av_interleaved_write_frame(si-\u003eavfc_e, si-\u003eavp_e) \u003c 0)\n+\t\t\t\t\tlwsl_err(\u0022%s: mux write fail\u005cn\u0022, __func__);\n+\t\t\t}\n+#endif\n+\n+\t\t\tsi-\u003eavp_e-\u003edata \u003d od;\n+\t\t\tsi-\u003eavp_e-\u003esize \u003d os;\n+\n+\t\t\tav_packet_unref(si-\u003eavp_e);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/minimal-examples/ws-server/minimal-ws-server-v4l2/v4l2.c b/minimal-examples/ws-server/minimal-ws-server-v4l2/v4l2.c\nnew file mode 100644\nindex 0000000..907c92c\n--- /dev/null\n+++ b/minimal-examples/ws-server/minimal-ws-server-v4l2/v4l2.c\n@@ -0,0 +1,574 @@\n+#include \u003clibwebsockets.h\u003e\n+\n+#include \u0022private.h\u0022\n+#include \u003csignal.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cassert.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003cerrno.h\u003e\n+\n+#include \u003csys/types.h\u003e\n+#include \u003csys/time.h\u003e\n+#include \u003csys/mman.h\u003e\n+#include \u003csys/ioctl.h\u003e\n+\n+#include \u003clinux/videodev2.h\u003e\n+\n+static int\n+xioctl(int fh, unsigned long request, void *arg)\n+{\n+\tint r;\n+\n+\tdo {\n+\t\tr \u003d ioctl(fh, request, arg);\n+\t} while (-1 \u003d\u003d r \u0026\u0026 EINTR \u003d\u003d errno);\n+\n+\treturn r;\n+}\n+\n+#if !defined(OWN_MOOF)\n+static int\n+avio_wcb(void* ptr, uint8_t* buf, int buf_size)\n+{\n+\tstruct src_inst *si \u003d (struct src_inst *)ptr;\n+\tstruct msg msg;\n+\n+\tmsg.len \u003d buf_size;\n+\tmsg.payload \u003d malloc(LWS_PRE + msg.len);\n+\n+//\tlwsl_notice(\u0022%s: %d (subs %d)\u005cn\u0022, __func__, buf_size, si-\u003esubsequent);\n+\n+\tif (!si-\u003esubsequent) {\n+#if 0\n+//\t\tlwsl_hexdump_notice(buf, buf_size);\n+\n+\t\tmemcpy(si-\u003emp4_hdr + LWS_PRE + si-\u003emp4_header_len,\n+\t\t buf, buf_size);\n+\t\tsi-\u003emp4_header_len +\u003d buf_size;\n+#endif\n+\t\treturn buf_size;\n+\t}\n+\n+\tif (!msg.payload)\n+\t\tlwsl_warn(\u0022%s: OOM underrun\u005cn\u0022, __func__);\n+\telse {\n+\t\tmemcpy(msg.payload + LWS_PRE, buf, buf_size);\n+//\t\tlwsl_hexdump_notice(buf, buf_size);\n+\n+//\t\tlwsl_hexdump_notice(msg.payload + LWS_PRE, buf_size);\n+\n+\t\tif (!lws_ring_insert(si-\u003ering, \u0026msg, 1)) {\n+\t\t\t__mirror_destroy_message(\u0026msg);\n+\t\t\tlwsl_notice(\u0022dropping!\u005cn\u0022);\n+\t\t}\n+\t}\n+\n+\treturn buf_size;\n+}\n+#endif\n+\n+int\n+create_si(struct raw_vhd *vhd, struct lws *wsi)\n+{\n+\tstruct v4l2_requestbuffers req;\n+\tstruct src_inst *si \u003d NULL;\n+\tint fd;\n+\n+\t/*\n+\t * Create the associated si\n+\t */\n+\n+\tsi \u003d malloc(sizeof(*si));\n+\tif (!si)\n+\t\tgoto bail1;\n+\n+\tmemset(si, 0, sizeof(*si));\n+\tsi-\u003ering \u003d lws_ring_create(sizeof(struct msg), QUEUELEN,\n+\t\t\t\t __mirror_destroy_message);\n+\tif (!si-\u003ering) {\n+\t\tfree(si);\n+\t\tgoto bail1;\n+\t}\n+\n+\tlws_snprintf(si-\u003ename, sizeof(si-\u003ename) - 1, \u0022%d\u0022, vhd-\u003eowner.count);\n+\n+\tlws_pthread_mutex_init(\u0026si-\u003elock);\n+\tlws_set_opaque_user_data(wsi, si);\n+\n+\tlws_dll2_add_tail(\u0026si-\u003elist, \u0026vhd-\u003eowner);\n+\n+\tlwsl_notice(\u0022Created new si '%s'\u005cn\u0022, si-\u003ename);\n+\n+\tfd \u003d (int)(intptr_t)lws_get_socket_fd(wsi);\n+\tsi-\u003efilefd \u003d fd;\n+\tif (init_device(si)) {\n+\t\tlwsl_err(\u0022%s: device init failed\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tsi-\u003ecapture_wsi \u003d wsi;\n+\n+\tmemset(\u0026req, 0, sizeof(req));\n+\n+\treq.count \u003d 8;\n+\treq.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\treq.memory \u003d V4L2_MEMORY_MMAP;\n+\n+\tif (xioctl(fd, VIDIOC_REQBUFS, \u0026req) \u003c 0) {\n+\t\tlwsl_err(\u0022%s: mmap req bufs failed\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\tif (req.count \u003c 2) {\n+\t\tlwsl_err(\u0022%s: Insufficient buffer memory\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\tsi-\u003ebuffers \u003d calloc(req.count, sizeof(*si-\u003ebuffers));\n+\n+\tif (!si-\u003ebuffers) {\n+\t\tlwsl_err(\u0022%s: OOM\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\tfor (si-\u003ebcount \u003d 0; si-\u003ebcount \u003c req.count; si-\u003ebcount++) {\n+\t\tstruct v4l2_buffer buf;\n+\n+\t\tmemset(\u0026buf, 0, sizeof(buf));\n+\n+\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n+\t\tbuf.index \u003d si-\u003ebcount;\n+\n+\t\tif (xioctl(fd, VIDIOC_QUERYBUF, \u0026buf) \u003c 0) {\n+\t\t\tlwsl_err(\u0022%s: querybuf failed\u005cn\u0022, __func__);\n+\n+\t\t\tgoto bail1;\n+\t\t}\n+\n+\t\tsi-\u003ebuffers[si-\u003ebcount].len \u003d buf.length;\n+\t\tsi-\u003ebuffers[si-\u003ebcount].payload \u003d (void *)\n+\t\t mmap(NULL /* start anywhere */,\n+\t\t\t\tbuf.length, PROT_READ | PROT_WRITE,\n+\t\t\t\tMAP_SHARED /* recommended */, fd,\n+\t\t\t\tbuf.m.offset);\n+\n+\t\tif (si-\u003ebuffers[si-\u003ebcount].payload \u003d\u003d MAP_FAILED) {\n+\t\t\tlwsl_err(\u0022%s: map failed\u005cn\u0022, __func__);\n+\t\t\tgoto bail1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+\n+bail1:\n+\t\tfree(si-\u003ebuffers);\n+\t\tsi-\u003ebuffers \u003d NULL;\n+\n+\t\treturn 1;\n+}\n+\n+int\n+init_device(struct src_inst *si)\n+{\n+\tstruct v4l2_cropcap cropcap;\n+//\tstruct v4l2_queryctrl ctrl;\n+\tstruct v4l2_capability cap;\n+\tstruct v4l2_format fmt;\n+\tstruct v4l2_crop crop;\n+\tstruct v4l2_input inp;\n+\tstruct v4l2_streamparm parm;\n+\tint n \u003d 0, fd \u003d si-\u003efilefd;\n+\n+\tif (xioctl(fd, VIDIOC_S_INPUT, \u0026n) \u003d\u003d -1)\n+\t\treturn 1;\n+\n+\tif (xioctl(fd, VIDIOC_QUERYCAP, \u0026cap)) {\n+\t\tlwsl_err(\u0022%s: QUERYCAP failed\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tlwsl_notice(\u0022cap 0x%x, bus_info %s, driver %s, card %s\u005cn\u0022,\n+\t\t(int)cap.capabilities, cap.bus_info, cap.driver, cap.card);\n+\n+\tif (!(cap.capabilities \u0026 V4L2_CAP_VIDEO_CAPTURE)) {\n+\t\tlwsl_err(\u0022%s: Device not capable of capture\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tif (!(cap.capabilities \u0026 V4L2_CAP_STREAMING)) {\n+\t\tlwsl_err(\u0022%s: Device not capable of streaming\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\t/* Select video input, video standard and tune here. */\n+\n+\tmemset(\u0026inp, 0, sizeof(inp));\n+\tif (xioctl(fd, VIDIOC_ENUMINPUT, \u0026inp) !\u003d -1)\n+\t\tlwsl_notice(\u0022%d %s %d\u005cn\u0022, inp.index, inp.name, inp.type);\n+\n+\tmemset(\u0026cropcap, 0, sizeof(cropcap));\n+\tcropcap.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\n+\tif (!xioctl(fd, VIDIOC_CROPCAP, \u0026cropcap)) {\n+\t\tcrop.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\t\tcrop.c \u003d cropcap.defrect; /* reset to default */\n+\n+\t\txioctl(fd, VIDIOC_S_CROP, \u0026crop);\n+\t}\n+\n+\t/* indicate our preferred streaming rate */\n+\n+\tmemset(\u0026parm, 0, sizeof(parm));\n+\tparm.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\n+\tparm.parm.capture.timeperframe.numerator \u003d 1;\n+\tparm.parm.capture.timeperframe.denominator \u003d 30;\n+\n+\txioctl(fd, VIDIOC_S_PARM, \u0026parm);\n+\n+\t/* indicate our preferred frame type */\n+\n+\tmemset(\u0026fmt, 0, sizeof(fmt));\n+\n+\tfmt.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\n+\txioctl(fd, VIDIOC_G_FMT, \u0026fmt);\n+\n+\t/* if the source offers h.264 directly, take that... */\n+\tfmt.fmt.pix.pixelformat \u003d V4L2_PIX_FMT_MJPEG; // V4L2_PIX_FMT_H264;\n+\tfmt.fmt.pix.colorspace \u003d V4L2_COLORSPACE_DEFAULT;\n+\n+\t/* we indicate our preferred size, then... */\n+\n+\tfmt.fmt.pix.width \u003d 640;\n+\tfmt.fmt.pix.height \u003d 360;\n+\n+\tif (xioctl(fd, VIDIOC_S_FMT, \u0026fmt) \u003c 0) {\n+\t\tlwsl_err(\u0022%s: Can't set FMT\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tfmt.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\txioctl(fd, VIDIOC_G_FMT, \u0026fmt);\n+\n+\t/*...we align with the actual size the video source chose */\n+\n+\tsi-\u003ewidth \u003d fmt.fmt.pix.width;\n+\tsi-\u003eheight \u003d fmt.fmt.pix.height;\n+\n+\t/* get the frame rate that we ended up with too */\n+\n+\tparm.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\txioctl(fd, VIDIOC_G_PARM, \u0026parm);\n+\n+\tlwsl_notice(\u0022%d pixfmt: %c%c%c%c %d %d %d %d (%d/%d)\u005cn\u0022, fmt.type,\n+\t\t\t(fmt.fmt.pix.pixelformat) \u0026 0xff,\n+\t\t\t(fmt.fmt.pix.pixelformat \u003e\u003e 8) \u0026 0xff,\n+\t\t\t(fmt.fmt.pix.pixelformat \u003e\u003e 16) \u0026 0xff,\n+\t\t\t(fmt.fmt.pix.pixelformat \u003e\u003e 24) \u0026 0xff,\n+\t\t\tfmt.fmt.pix.width, fmt.fmt.pix.height,\n+\t\t\tfmt.fmt.pix.field, fmt.fmt.pix.sizeimage,\n+\t\t\tparm.parm.capture.timeperframe.numerator,\n+\t\t\tparm.parm.capture.timeperframe.denominator);\n+\n+\tif (fmt.fmt.pix.pixelformat !\u003d V4L2_PIX_FMT_H264) {\n+\n+\t\tlwsl_user(\u0022%s: initializing transcoding\u005cn\u0022, __func__);\n+\n+\t\t/*\n+\t\t * Most webcams and HDMI capture devices can only issue MJPEG\n+\t\t * at speed, we have to transcode it to h.264 for streaming\n+\t\t */\n+\n+\t\t/*\n+\t\t * Pipeline: MJPEG decode\n+\t\t */\n+\n+\t\tsi-\u003eavc_d \u003d avcodec_find_decoder(AV_CODEC_ID_MJPEG);\n+\t\tif (!si-\u003eavc_d)\n+\t\t\treturn -1;\n+\n+\t\tsi-\u003eavcpc_d \u003d av_parser_init(si-\u003eavc_d-\u003eid);\n+\n+\t\tsi-\u003eavcc_d \u003d avcodec_alloc_context3(si-\u003eavc_d);\n+\n+\t\tavcodec_get_context_defaults3(si-\u003eavcc_d, si-\u003eavc_d);\n+\n+\t\tsi-\u003eavcc_d-\u003ewidth\t\t\u003d si-\u003ewidth;\n+\t\tsi-\u003eavcc_d-\u003eheight\t\t\u003d si-\u003eheight;\n+\t\tsi-\u003eavcc_d-\u003etime_base\t\t\u003d (AVRational){ 1, 1000000 };\n+\t\tsi-\u003eavcc_d-\u003eframerate\t\t\u003d (AVRational){\n+\t\t\t\tparm.parm.capture.timeperframe.denominator,\n+\t\t\t\tparm.parm.capture.timeperframe.numerator\n+\t\t};\n+\t//\tsi-\u003eavcc_d-\u003epix_fmt\t\t\u003d AV_PIX_FMT_YUV420P;\n+\n+\t\tavcodec_open2(si-\u003eavcc_d, si-\u003eavc_d, NULL);\n+\n+\t\tsi-\u003eavf_d \u003d av_frame_alloc();\n+\t\tif (!si-\u003eavf_d)\n+\t\t\treturn -1;\n+\n+\t\tsi-\u003eavp_d \u003d av_packet_alloc();\n+\t\tif (!si-\u003eavp_d)\n+\t\t return -1;\n+\n+\t\t/*\n+\t\t * Pipeline: H.264 encode\n+\t\t */\n+\n+\t\tsi-\u003eavc_e \u003d avcodec_find_encoder_by_name(\u0022h264_omx\u0022);\n+\t\tif (!si-\u003eavc_e) {\n+\t\t\tsi-\u003eavc_e \u003d avcodec_find_encoder(AV_CODEC_ID_H264);\n+\t\t\tif (!si-\u003eavc_e) {\n+\t\t\t\tlwsl_err(\u0022%s: we don't have an H264 enc\u005cn\u0022, __func__);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tsi-\u003eavcc_e \u003d avcodec_alloc_context3(si-\u003eavc_e);\n+\t\tif (!si-\u003eavcc_e) {\n+\t\t\tlwsl_err(\u0022%s: unable to instantiate H264 encoder\u005cn\u0022,\n+\t\t\t\t\t__func__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tsi-\u003eavp_e \u003d av_packet_alloc();\n+\t\tif (!si-\u003eavp_e)\n+\t\t return -1;\n+\n+#if !defined(OWN_MOOF)\n+\t\t/* avcc_e packet abstraction */\n+\n+\t\tsi-\u003eavioc_e \u003d avio_alloc_context(si-\u003eiobuf, sizeof(si-\u003eiobuf), 1,\n+\t\t\t\t\t\t si, NULL, avio_wcb, NULL);\n+\t\tif (!si-\u003eavioc_e) {\n+\t\t\tlwsl_err(\u0022%s: unable to create io ctx\u005cn\u0022, __func__);\n+\t\t\treturn -1;\n+\t\t}\n+#endif\n+\t\tif (avformat_alloc_output_context2(\u0026si-\u003eavfc_e, NULL,\n+\t\t\t\t\t\t \u0022ismv\u0022, NULL) \u003c 0) {\n+\t\t\tlwsl_err(\u0022%s: unable to alloc output ctx\u005cn\u0022, __func__);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tsi-\u003eavfc_e-\u003epb \u003d si-\u003eavioc_e;\n+\n+\t\tsi-\u003eavs_e \u003d avformat_new_stream(si-\u003eavfc_e, si-\u003eavc_e);\n+\t\tif (!si-\u003eavs_e) {\n+\t\t\tlwsl_err(\u0022%s: failed to get stream\u005cn\u0022, __func__);\n+\t\t}\n+\t\tsi-\u003eavs_e-\u003etime_base\t\t\u003d si-\u003eavcc_d-\u003etime_base;\n+\t\t//si-\u003eavs_e-\u003er_frame_rate\t\u003d si-\u003eavcc_d-\u003eframerate;\n+\t\tsi-\u003eavs_e-\u003ecodecpar-\u003ecodec_type\t\u003d AVMEDIA_TYPE_VIDEO;\n+\t\tsi-\u003eavs_e-\u003ecodecpar-\u003ecodec_id\t\u003d AV_CODEC_ID_H264;\n+\t\tsi-\u003eavs_e-\u003ecodecpar-\u003ecodec_tag\t\u003d V4L2_PIX_FMT_H264_NO_SC;\n+\t\tsi-\u003eavs_e-\u003ecodecpar-\u003ewidth\t\u003d si-\u003ewidth;\n+\t\tsi-\u003eavs_e-\u003ecodecpar-\u003eheight\t\u003d si-\u003eheight;\n+\t\tsi-\u003eavs_e-\u003eavg_frame_rate\t\u003d si-\u003eavcc_d-\u003eframerate;\n+\n+\t\tsi-\u003eavcc_e-\u003ebit_rate\t\t\u003d 8000000;\n+\t\tsi-\u003eavcc_e-\u003ewidth\t\t\u003d si-\u003ewidth;\n+\t\tsi-\u003eavcc_e-\u003eheight\t\t\u003d si-\u003eheight;\n+\t\tsi-\u003eavcc_e-\u003etime_base\t\t\u003d si-\u003eavcc_d-\u003etime_base;\n+\t\tsi-\u003eavcc_e-\u003eframerate\t\t\u003d si-\u003eavcc_d-\u003eframerate;\n+\t\tsi-\u003eavcc_e-\u003egop_size\t\t\u003d 3;\n+\t\tsi-\u003eavcc_e-\u003emax_b_frames\t\u003d 3;\n+\t\tsi-\u003eavcc_e-\u003epix_fmt\t\t\u003d AV_PIX_FMT_YUV420P;\n+\n+\t\tsi-\u003eavcc_e-\u003eticks_per_frame\t\u003d 2;\n+\n+\t\tav_dict_set(\u0026si-\u003eopt, \u0022profile\u0022, \u0022main\u0022, 0);\n+\t\tav_dict_set(\u0026si-\u003eopt, \u0022tune\u0022, \u0022zerolatency\u0022, 0);\n+\t\tav_dict_set(\u0026si-\u003eopt, \u0022movflags\u0022, \u0022\u0022\n+\t\t\t\t\t\t \u0022isml\u0022\n+\t\t\t\t\t\t \u0022+faststart\u0022\n+\t\t\t\t\t\t \u0022+frag_keyframe\u0022\n+\t\t\t\t\t\t //\u0022+empty_moov\u0022\n+\t\t\t\t\t\t //\u0022+separate_moof\u0022\n+\t\t\t\t\t\t //\u0022+delay_moov\u0022\n+\t\t\t\t\t\t \u0022+default_base_moof\u0022\n+\t\t\t\t, 0);\n+\t\tav_opt_set_dict(si-\u003eavcc_e-\u003epriv_data, \u0026si-\u003eopt);\n+\t\tav_opt_set_dict(si-\u003eavcc_e, \u0026si-\u003eopt);\n+\n+\t\tn \u003d avcodec_open2(si-\u003eavcc_e, si-\u003eavc_e, NULL);\n+\t\tif (n \u003c 0) {\n+\t\t\tlwsl_err(\u0022%s: can't open codec: err %d\u005cn\u0022, __func__, n);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tsi-\u003eavf_d-\u003eformat\t\t\u003d AV_PIX_FMT_YUV420P;\n+\t\tsi-\u003eavf_d-\u003ewidth\t\t\u003d si-\u003ewidth;\n+\t\tsi-\u003eavf_d-\u003eheight\t\t\u003d si-\u003eheight;\n+\n+\t\tn \u003d av_frame_get_buffer(si-\u003eavf_d, /* alignment */ 32);\n+\t\tif (n \u003c 0) {\n+\t\t\tlwsl_err(\u0022Could not allocate the video frame data\u005cn\u0022);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+#if 0\n+\t/* Try the extended control API first */\n+\n+\tctrl.id \u003d V4L2_CTRL_FLAG_NEXT_CTRL;\n+\tif (!xioctl (fd, VIDIOC_QUERYCTRL, \u0026ctrl)) {\n+\t\tdo {\n+\t\t\tfprintf(stderr, \u0022%s\u005cn\u0022, ctrl.name);\n+\t\t\t//\tmw-\u003eadd_control(ctrl, fd, grid, gridLayout);\n+\t\t\tctrl.id |\u003d V4L2_CTRL_FLAG_NEXT_CTRL;\n+\t\t} while (!xioctl (fd, VIDIOC_QUERYCTRL, \u0026ctrl));\n+\t}\n+#endif\n+\n+\treturn 0;\n+}\n+\n+int\n+deinit_device(struct src_inst *si)\n+{\n+\tint n;\n+\n+\tfor (n \u003d 0; n \u003c (int)si-\u003ebcount; n++)\n+\t\tmunmap(si-\u003ebuffers[n].payload, si-\u003ebuffers[n].len);\n+\n+\tfree(si-\u003ebuffers);\n+\tlws_dll2_remove(\u0026si-\u003elist);\n+\n+\tif (si-\u003efilefd !\u003d -1)\n+\t\tclose(si-\u003efilefd);\n+\n+\tif (si-\u003eavfc_e)\n+\t\tavformat_free_context(si-\u003eavfc_e);\n+\n+\tif (si-\u003eavcc_e)\n+\t\tavcodec_free_context(\u0026si-\u003eavcc_e);\n+\tif (si-\u003eavp_e)\n+\t\tav_packet_free(\u0026si-\u003eavp_e);\n+\n+\tif (si-\u003eavcpc_d)\n+\t\tav_parser_close(si-\u003eavcpc_d);\n+\n+\tif (si-\u003eavcc_d) {\n+\t\tavcodec_close(si-\u003eavcc_d);\n+\t\tavcodec_free_context(\u0026si-\u003eavcc_d);\n+\t}\n+\tif (si-\u003eavf_d)\n+\t\tav_frame_free(\u0026si-\u003eavf_d);\n+\tif (si-\u003eavp_d)\n+\t\tav_packet_free(\u0026si-\u003eavp_d);\n+\n+//\t\tif (si-\u003eswsc)\n+//\t\t\tsws_freeContext(si-\u003eswsc);\n+\n+\tif (si-\u003eavs_e)\n+\t\tav_packet_free(\u0026si-\u003eavp_d);\n+\n+\tlws_ring_destroy(si-\u003ering);\n+\tlws_pthread_mutex_destroy(\u0026si-\u003elock);\n+\tfree(si);\n+\n+\treturn 0;\n+}\n+\n+int\n+start_capturing(struct src_inst *si)\n+{\n+\tunsigned int i;\n+\tenum v4l2_buf_type type;\n+\n+\tfor (i \u003d 0; i \u003c si-\u003ebcount; ++i) {\n+\t\tstruct v4l2_buffer buf;\n+\n+\t\tmemset(\u0026buf, 0, sizeof(buf));\n+\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n+\t\tbuf.index \u003d i;\n+\n+\t\tif (xioctl(si-\u003efilefd, VIDIOC_QBUF, \u0026buf) \u003c 0) {\n+\t\t\tlwsl_warn(\u0022%s: unable to start cap %d\u005cn\u0022, __func__,\n+\t\t\t\t\tsi-\u003efilefd);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\tlwsl_notice(\u0022%s: stream on\u005cn\u0022, __func__);\n+\n+\tlws_rx_flow_control(si-\u003ecapture_wsi, 1);\n+\n+\ttype \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\treturn xioctl(si-\u003efilefd, VIDIOC_STREAMON, \u0026type) \u003d\u003d -1;\n+}\n+\n+int\n+stop_capturing(struct src_inst *si)\n+{\n+\tenum v4l2_buf_type type;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tlws_rx_flow_control(si-\u003ecapture_wsi, 0);\n+\n+\tlwsl_notice(\u0022%s\u005cn\u0022, __func__);\n+\ttype \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\n+\tfor (i \u003d 0; i \u003c si-\u003ebcount; ++i) {\n+\t\tstruct v4l2_buffer buf;\n+\n+\t\tmemset(\u0026buf, 0, sizeof(buf));\n+\t\tbuf.type \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\t\tbuf.memory \u003d V4L2_MEMORY_MMAP;\n+\t\tbuf.index \u003d i;\n+\n+\t\txioctl(si-\u003efilefd, VIDIOC_DQBUF, \u0026buf);\n+\t}\n+\n+\tret \u003d xioctl(si-\u003efilefd, VIDIOC_STREAMOFF, \u0026type);\n+\tif (ret \u003c 0)\n+\t\tlwsl_err(\u0022%s: failed to stop stream\u005cn\u0022, __func__);\n+\n+\treturn ret \u003d\u003d -1;\n+}\n+\n+int\n+lws_v4l2_dq(struct src_inst *si, struct v4l2_buffer *buf)\n+{\n+\tmemset(buf, 0, sizeof(*buf));\n+\n+\tbuf-\u003etype \u003d V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\tbuf-\u003ememory \u003d V4L2_MEMORY_MMAP;\n+\n+\tif (xioctl(si-\u003efilefd, VIDIOC_DQBUF, buf) \u003c 0) {\n+\t\tif (errno \u003d\u003d EAGAIN)\n+\t\t\treturn 0;\n+\n+\t\tlwsl_warn(\u0022%s: DQBUF ioctl fail: %d\u005cn\u0022, __func__, errno);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tassert(buf-\u003eindex \u003c si-\u003ebcount);\n+\n+\treturn 0;\n+}\n+\n+int\n+lws_v4l2_q(struct src_inst *si, struct v4l2_buffer *buf)\n+{\n+\tif (xioctl(si-\u003efilefd, VIDIOC_QBUF, buf) \u003c 0) {\n+\t\tlwsl_err(\u0022%s: QBUF failed\u005cn\u0022, __func__);\n+\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n","s":{"c":1746414822,"u": 16593}} ],"g": 26148,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}