Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Live streams can be read from the server with:

| Protocol | Variants | Video Codecs | Audio Codecs |
|----------|----------|--------------|--------------|
| HLS | fMP4/mpeg-ts/Low Latency | H264, H265, AV1 | MPEG-4(AAC) |
| HLS | fMP4/mpeg-ts/Low Latency | H264, H265, AV1 | MPEG-4(AAC), Opus |
| RTMP | - | H264, H265, AV1 | MPEG-4(AAC), G711(PCMA/PCMU), Opus |

## Usage and configuration
Expand Down
2 changes: 1 addition & 1 deletion lib/shinkai/sink/hls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Shinkai.Sink.Hls do
alias HLX.Writer
alias Phoenix.PubSub

@supported_codecs [:h264, :h265, :av1, :aac]
@supported_codecs [:h264, :h265, :av1, :aac, :opus]

def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: opts[:name])
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defmodule Shinkai.MixProject do
[
{:phoenix_pubsub, "~> 2.2"},
{:rtsp, "~> 0.8.0"},
{:hlx, "~> 0.5.0"},
{:hlx, "~> 0.6.0"},
{:ex_rtmp, "~> 0.4.1"},
{:yaml_elixir, "~> 2.12"},
{:burrito, "~> 1.5.0"},
Expand Down
6 changes: 3 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
"elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"},
"ex_doc": {:hex, :ex_doc, "0.40.0", "2635974389b80fd3ca61b0f993d459dad05b4a8f9b069dcfbbc5f6a8a6aef60e", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "c040735250e2752b6e1102eeb4aa3f1dca74c316db873ae09f955d42136e7e5b"},
"ex_flv": {:hex, :ex_flv, "0.4.0", "9e43c833b5cbe3c6e21bb2651ae7650f3ec939eac8079f34efed8f813bf9133d", [:mix], [], "hexpm", "484f6990791e0c8862a88e4150f004deb067c7342e40b779c82d5c9a9c057969"},
"ex_m3u8": {:hex, :ex_m3u8, "0.15.4", "66f6ec7e4fb7372c48032db1c2d4a3e6c2bbbde2d1d9a1098986e3caa0ab7a55", [:mix], [{:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "ec03aa516919e0c8ec202da55f609b763bd7960195a3388900090fcad270c873"},
"ex_m3u8": {:hex, :ex_m3u8, "0.16.0", "29fc405a790d95bb320cbbbbaa1d6c2608f043bce02ad8d35e6489497ca4b2a0", [:mix], [{:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "a98a149c14b3929cfe163fd9d23edfa86ab0bc72bee5c1945e9dab058aa051d7"},
"ex_mp4": {:hex, :ex_mp4, "0.14.2", "c362b27c50fa8d5a16e4f5652963fcc47d5a61215eb729a0d6f8ec521575ed6d", [:mix], [{:media_codecs, "~> 0.10.0", [hex: :media_codecs, repo: "hexpm", optional: true]}, {:ratio, "~> 4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:table_rex, "~> 4.0", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "3712c62a93ddde83419bb22e382a145c6527c8b002d8a22348202828022e1041"},
"ex_rtcp": {:hex, :ex_rtcp, "0.4.1", "e0f0c0baf329de92059e2afdc34d66d61f8b983f0801daa10f1a712360919e45", [:mix], [], "hexpm", "83ab3dffcffc6149eb404f1e1b1b62f755efd54c818e27c2c88e6ba3341c5d41"},
"ex_rtmp": {:hex, :ex_rtmp, "0.4.1", "1be8a1f75f2940d59ae07939218d1cdddac85de118370f0f001f816b8bac4576", [:mix], [{:ex_flv, "~> 0.4.0", [hex: :ex_flv, repo: "hexpm", optional: false]}], "hexpm", "58e1f993c575b6604e8256ed89887fcb7f7b80d0627e0bd71e9eea98bbe79766"},
"ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"},
"ex_sdp": {:hex, :ex_sdp, "1.1.2", "7e7465cb13b557cc76ef3e854bad7626b73cc1d1f480d38b5fbcf539c7d8a45d", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "50a27c2d745924679acca32b3d5499d0b35d135a180b83422df82c289afce564"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"hlx": {:hex, :hlx, "0.5.0", "22542ba77c4fd9a50d4566e546ff49b19d9b22d5110bf3b920d3d1f5f61ca9c1", [:mix], [{:ex_m3u8, "~> 0.15.0", [hex: :ex_m3u8, repo: "hexpm", optional: false]}, {:ex_mp4, "~> 0.14.0", [hex: :ex_mp4, repo: "hexpm", optional: false]}, {:media_codecs, "~> 0.10.0", [hex: :media_codecs, repo: "hexpm", optional: false]}, {:mpeg_ts, "~> 3.3.5", [hex: :mpeg_ts, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "74bcb44fda7a2407b37c125385b2389b7922438aba05b13bbc6ab01f8ba42a70"},
"finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"},
"hlx": {:hex, :hlx, "0.6.0", "bbe81afa5f95f869633d92dfc55b908ce8da0e3f72183d98471994c87cd92e59", [:mix], [{:ex_m3u8, "~> 0.16.0", [hex: :ex_m3u8, repo: "hexpm", optional: false]}, {:ex_mp4, "~> 0.14.0", [hex: :ex_mp4, repo: "hexpm", optional: false]}, {:media_codecs, "~> 0.10.0", [hex: :media_codecs, repo: "hexpm", optional: false]}, {:mpeg_ts, "~> 3.3.5", [hex: :mpeg_ts, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "7da77127e408b20c736ac8281a6a1e9b336df712c711cb46f62c6d03db9539fb"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
Expand All @@ -38,8 +38,8 @@
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"},
"ratio": {:hex, :ratio, "4.0.1", "3044166f2fc6890aa53d3aef0c336f84b2bebb889dc57d5f95cc540daa1912f8", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "c60cbb3ccdff9ffa56e7d6d1654b5c70d9f90f4d753ab3a43a6bf40855b881ce"},
"rtsp": {:hex, :rtsp, "0.8.1", "4bffebfcb0e1354283567178c040bbf40a85c4fbbde6d23addbbc7672cb3c700", [:mix], [{:ex_mp4, "~> 0.14.0", [hex: :ex_mp4, repo: "hexpm", optional: true]}, {:ex_rtcp, "~> 0.4.0", [hex: :ex_rtcp, repo: "hexpm", optional: false]}, {:ex_rtp, "~> 0.4.0", [hex: :ex_rtp, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 1.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:media_codecs, "~> 0.10.0", [hex: :media_codecs, repo: "hexpm", optional: false]}, {:membrane_rtsp, "~> 0.11.0", [hex: :membrane_rtsp, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "b4af3c30b8f79dd642940452c6ad6727bfd1df492e5ddbc1eb705f25df6f4053"},
"req": {:hex, :req, "0.5.16", "99ba6a36b014458e52a8b9a0543bfa752cb0344b2a9d756651db1281d4ba4450", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "974a7a27982b9b791df84e8f6687d21483795882a7840e8309abdbe08bb06f09"},
"rtsp": {:hex, :rtsp, "0.8.1", "4bffebfcb0e1354283567178c040bbf40a85c4fbbde6d23addbbc7672cb3c700", [:mix], [{:ex_mp4, "~> 0.14.0", [hex: :ex_mp4, repo: "hexpm", optional: true]}, {:ex_rtcp, "~> 0.4.0", [hex: :ex_rtcp, repo: "hexpm", optional: false]}, {:ex_rtp, "~> 0.4.0", [hex: :ex_rtp, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 1.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:media_codecs, "~> 0.10.0", [hex: :media_codecs, repo: "hexpm", optional: false]}, {:membrane_rtsp, "~> 0.11.0", [hex: :membrane_rtsp, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "b4af3c30b8f79dd642940452c6ad6727bfd1df492e5ddbc1eb705f25df6f4053"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
"typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
Expand Down
22 changes: 9 additions & 13 deletions test/shinkai/pipeline_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ defmodule Shinkai.PipelineTest do
assert_receive {:hls, :done}, 5_000

hls_path = Path.join(Shinkai.Config.get_config(:hls)[:storage_dir], source.id)
audio? = not String.contains?(unquote(fixture), "opus")
assert_hls(hls_path, audio?)
assert_hls(hls_path, true)

on_exit(fn -> File.rm_rf!(hls_path) end)
end
Expand All @@ -56,8 +55,7 @@ defmodule Shinkai.PipelineTest do
assert_receive {:hls, :done}, 5_000

hls_path = Path.join(Shinkai.Config.get_config(:hls)[:storage_dir], id)
audio? = not String.contains?(unquote(fixture), "opus")
assert_hls(hls_path, audio?)
assert_hls(hls_path, true)

File.rm_rf!(hls_path)
end
Expand All @@ -78,8 +76,7 @@ defmodule Shinkai.PipelineTest do
assert_receive {:hls, :done}, 5_000

hls_path = Path.join(Shinkai.Config.get_config(:hls)[:storage_dir], source.id)
audio? = not String.contains?(unquote(fixture), "opus")
assert_hls(hls_path, audio?)
assert_hls(hls_path, true)

ExRTMP.Server.stop(rtmp_server)
File.rm_rf!(hls_path)
Expand Down Expand Up @@ -121,8 +118,6 @@ defmodule Shinkai.PipelineTest do
source = %Source{id: "live-#{id}", type: :rtmp, uri: rtmp_uri(rtmp_server, "live/#{id}")}
start_source(source)

# _pid = start_supervised!({Shinkai.Pipeline, source})

{:ok, pid} = ExRTMP.Client.start_link(uri: rtmp_uri(rtmp_server, "live"), stream_key: id)
assert :ok = ExRTMP.Client.connect(pid)
assert :ok = ExRTMP.Client.play(pid)
Expand Down Expand Up @@ -168,7 +163,6 @@ defmodule Shinkai.PipelineTest do
Enum.find(items, &is_struct(&1, ExM3U8.Tags.Media))
end

# codesc "avc1.42C00C,mp4a.40.2"
if audio? do
assert %{audio: "audio", codecs: _codecs, resolution: {240, 136}} =
Enum.find(items, &is_struct(&1, ExM3U8.Tags.Stream))
Expand All @@ -178,10 +172,10 @@ defmodule Shinkai.PipelineTest do
end

if audio? do
assert_media_playlist(hls_path, "audio", 3, 5)
assert_media_playlist(hls_path, "audio", 2..3, 5)
end

assert_media_playlist(hls_path, "video", 2, 5)
assert_media_playlist(hls_path, "video", 2..2, 5)
end

defp assert_rtmp_receive(pid, fixture) do
Expand Down Expand Up @@ -218,7 +212,7 @@ defmodule Shinkai.PipelineTest do
"rtmp://127.0.0.1:#{port}/#{path}"
end

defp assert_media_playlist(hls_path, variant, target_duration, segments_count) do
defp assert_media_playlist(hls_path, variant, expected_target_duration, segments_count) do
assert {:ok, playlist} =
hls_path
|> Path.join("#{variant}.m3u8")
Expand All @@ -227,11 +221,13 @@ defmodule Shinkai.PipelineTest do

assert %ExM3U8.MediaPlaylist{
info: %ExM3U8.MediaPlaylist.Info{
target_duration: ^target_duration
target_duration: target_duration
},
timeline: timeline
} = playlist

assert target_duration in expected_target_duration

assert %Tags.MediaInit{uri: init_uri} = Enum.at(timeline, 0)
segments = Enum.filter(timeline, &is_struct(&1, Tags.Segment))
assert length(segments) == segments_count
Expand Down
Loading