Skip to content

Commit fae5342

Browse files
author
devalexqt
committed
Init
1 parent 30b3b97 commit fae5342

File tree

7 files changed

+395
-64
lines changed

7 files changed

+395
-64
lines changed

README.md

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,143 @@
1-
# pi_h264
1+
##Live real time stream with no delay from Raspberry Pi to browser
2+
based on boradway https://github.com/mbebenita/Broadway h264 decoder.
3+
4+
In browser it use broadway h264 software decoder to decode NAL h264 packets and rende decoded frame to html canvas.
5+
For receive NAL h264 baseline packets from server (Raspberry Pi) it use websocket over sockets.io.
6+
On server it use raspberry camera for get NAL baseline h264 packets from spawned process and send it over sockets.io.
7+
8+
```
9+
var proc = spawn('raspivid', [
10+
'-t', '0',
11+
'-o', '-',// out h264 to std out
12+
"-n",
13+
'-w', 640,
14+
'-h', 360,
15+
'-fps', 30,
16+
'-pf', "baseline"//only accepted profile for decoder
17+
]);
18+
```
19+
Is it possible to use ffmpeg 3.2 with h264_omx support (need modify libavcodec/omx.c for enable baseline h264 profile, because default is High profile).
20+
21+
ADD to libavcodec/omx.c at line 518 v3.2 for support baseline profile
22+
!!!Do not need configure again if you already have compiled ffmpeg, just run make command!!!
23+
```
24+
avc.eProfile=OMX_VIDEO_AVCProfileBaseline;
25+
```
26+
Example:
27+
```
28+
if (avctx->codec->id == AV_CODEC_ID_H264) {
29+
OMX_VIDEO_PARAM_AVCTYPE avc = { 0 };
30+
INIT_STRUCT(avc);
31+
avc.nPortIndex = s->out_port;
32+
err = OMX_GetParameter(s->handle, OMX_IndexParamVideoAvc, &avc);
33+
CHECK(err);
34+
avc.nBFrames = 0;
35+
avc.nPFrames = avctx->gop_size - 1;
36+
//add
37+
avc.eProfile=OMX_VIDEO_AVCProfileBaseline;/////////////////////////////// change h264 profile to baseline
38+
//avc.eLevel=OMX_VIDEO_AVCLevel3;//////////////////////////////////////// change level if you want
39+
//add
40+
err = OMX_SetParameter(s->handle, OMX_IndexParamVideoAvc, &avc);
41+
CHECK(err);
42+
}
43+
```
44+
Possible profile values:
45+
```
46+
OMX_VIDEO_AVCProfileBaseline Baseline profile
47+
OMX_VIDEO_AVCProfileMain Main profile
48+
OMX_VIDEO_AVCProfileExtended Extended profile
49+
OMX_VIDEO_AVCProfileHigh High profile
50+
OMX_VIDEO_AVCProfileHigh10 High 10 profile
51+
OMX_VIDEO_AVCProfileHigh422 High 4:2:2 profile
52+
OMX_VIDEO_AVCProfileHigh444 High 4:4:4 profile
53+
OMX_VIDEO_AVCProfileMax
54+
```
55+
Possible level value:
56+
```
57+
OMX_VIDEO_AVCLevel1 Level 1
58+
OMX_VIDEO_AVCLevel1b Level 1b
59+
OMX_VIDEO_AVCLevel11 Level 1.1
60+
OMX_VIDEO_AVCLevel12 Level 1.2
61+
OMX_VIDEO_AVCLevel13 Level 1.3
62+
OMX_VIDEO_AVCLevel2 Level 2
63+
OMX_VIDEO_AVCLevel21 Level 2.1
64+
OMX_VIDEO_AVCLevel22 Level 2.2
65+
OMX_VIDEO_AVCLevel3 Level 3
66+
OMX_VIDEO_AVCLevel31 Level 3.1
67+
OMX_VIDEO_AVCLevel32 Level 3.2
68+
OMX_VIDEO_AVCLevel4 Level 4
69+
OMX_VIDEO_AVCLevel41 Level 4.1
70+
OMX_VIDEO_AVCLevel42 Level 4.2
71+
OMX_VIDEO_AVCLevel5 Level 5
72+
OMX_VIDEO_AVCLevel51 Level 5.1
73+
OMX_VIDEO_AVCLevelMax
74+
```
75+
76+
```
77+
>./configure --disable-encoders --enable-encoder='aac,h264_omx,mjpeg,libx264' --disable-decoders --enable-decoder='rawvideo,mjpeg,aac,h264_mmal' --enable-libfreetype --enable-static --enable-mmal --enable-omx-rpi --enable-yasm --enable-nonfree --enable-gpl --disable-doc
78+
79+
>make -j 4
80+
81+
>sudo make install
82+
//comilation time 2h-3h
83+
```
84+
After check ffmpeg decoder and encoders:
85+
```
86+
ffmpeg -decoders
87+
V..... h264_mmal h264 (mmal) (codec h264)
88+
V....D mjpeg MJPEG (Motion JPEG)
89+
V..... rawvideo raw video
90+
A....D aac AAC (Advanced Audio Coding)
91+
```
92+
```
93+
V..... h264_omx OpenMAX IL H.264 video encoder (codec h264)
94+
VFS... mjpeg MJPEG (Motion JPEG)
95+
A..... aac AAC (Advanced Audio Coding)
96+
```
97+
98+
Spawn ffmpeg for get h264 stream from Raspberry Pi camera:
99+
```
100+
var proc=spawn("ffmpeg",[
101+
"-s","640x360",
102+
"-re",
103+
"-framerate","30",
104+
"-pixel_format","yuv420p",//"yuv420p",//yuyv422
105+
"-i","/dev/video0",
106+
// "-c:v","h264_mmal",
107+
// "-i","/home/pi/360.mp4",
108+
"-c:v","h264_omx",
109+
"-b:v","1M",
110+
"-s","640x360",
111+
//"-s","1920x1080",
112+
"-an",
113+
//"-profile:v","baseline",//baseline
114+
//"-vf","drawtext='fontfile=/home/pi/ffmpeg/freefont/FreeSans.ttf:text=%{localtime\}':fontsize=50:fontcolor=yellow@1:box=1:[email protected]:x=(w-tw)/2:y=10",
115+
"-loglevel","error",
116+
"-stats",
117+
"-tune","zerolatency",
118+
"-f","h264",
119+
//"-reset_timestamps", "1",
120+
//"-movflags","isml+empty_moov+faststart",//+faststart//"frag_keyframe+empty_moov",
121+
//"-fflags","nobuffer",
122+
//"-frag_duration","5",
123+
"-y",
124+
//"cam_video.mp4"
125+
"-"
126+
])
127+
```
128+
With -vf (video filter option) you can write text, time, etc on video frame encoded in h264!
129+
130+
Raw stream from spawned procees must be parsed as separate NAL units and sended over socket to client.
131+
```
132+
var rawstream=proc.stdout.pipe(new Split(NALseparator))
133+
134+
rawstream.on("data",function(data){
135+
socket.emit("nal_packet",Buffer.concat([NALseparator, data]))
136+
})
137+
138+
```
139+
140+
##Client (html)
141+
Open page http://_raspberry_ip:8080
142+
143+
After page was loaded socket connected to server and now you can push start/stop button to start/stop live stream.

server.js

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ var http=require("http")
22
var os=require("os")
33
var netInt=os.networkInterfaces()
44
var fs=require("fs")
5-
var ffmpeg=require("./ffmpeg_stream.js")
6-
var WebSocket=require("ws")
5+
var streamer=require("./streamer.js")
6+
//var WebSocket=require("ws")
77

88
var server=http.createServer(function(req,res){
99
console.log("==>new connection: "+req.connection.remoteAddress+", url: "+req.url)
@@ -21,54 +21,74 @@ if(method=="get"&&req.url!="/videostream.mp4"){
2121
}//if no videostream
2222
else if(method=="get"&&req.url=="/videostream.mp4"){
2323
console.log("==>video stream request....")
24-
// res.writeHead(206, {
25-
// 'Transfer-Encoding': 'chunked'
26-
// , 'Content-Type': 'video/mp4'
27-
// //, 'Content-Length': 5000000000 // large size to fake a file
28-
// //, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
29-
// });
30-
//var ff_stream=new ffmpeg.start(res)
31-
32-
3324
}//videostream
3425

3526
}).listen(8080)
3627

37-
var ff_stream
28+
var stream
29+
30+
//socket.io
31+
var io = require('socket.io')(server)
32+
33+
io.on('connection', function (socket) {
34+
socket.emit('data', { data: 'Hi from server' })
35+
36+
socket.on('data', function (data) {
37+
console.log(data);
38+
})
39+
40+
socket.on("start_stream",function(data){
41+
console.log("==>start_stream")
42+
startStream(socket)
43+
})//start stream
44+
45+
socket.on("stop_stream",function(data){
46+
console.log("==>stop_stream")
47+
stopStream()
48+
})//start stream
49+
50+
51+
})//io
52+
53+
54+
function startStream(socket){
55+
console.log("==>starting stream...")
56+
stream=new streamer.start(socket)
57+
}//startStream
58+
function stopStream(socket){
59+
console.log("==>stoping stream...")
60+
try{
61+
stream.kill()
62+
}catch(err){console.log(err)}
63+
}//startStream
3864
//websocket
39-
var wsServer=new WebSocket.Server({server})//{port:8081}
65+
//var wsServer=new WebSocket.Server({server})//{port:8081}
4066

41-
wsServer.on("connection",function(ws){
42-
console.log("==>new ws client: ")
43-
//ws.send("Same data!")
44-
//new client
45-
ws.send(JSON.stringify({
46-
action : "init",
47-
width : 640,
48-
height : 360,
49-
}));
50-
51-
ws.on("message",function(data){
52-
console.log("==>new ws message: "+data.toString())
53-
//ff_stream=new ffmpeg.start(wsServer)
54-
55-
var j_data=JSON.parse(data.toString())
56-
if(j_data.action=="start_video"){
57-
console.log("==>starting video")
58-
ff_stream=new ffmpeg.start(wsServer)
59-
}//start video
60-
if(j_data.action=="stop_video"){
61-
console.log("==>stoping video")
62-
try{ff_stream.kill()}
63-
catch(e){console.log(e)}
64-
}//start video
65-
})//
66-
67-
ws.on("close",function(){
68-
console.log("==>ws client closed: ")
69-
})//
67+
// wsServer.on("connection",function(ws){
68+
// console.log("==>new ws client: ")
69+
70+
71+
// ws.on("message",function(data){
72+
// console.log("==>new ws message: "+data.toString())
73+
// //ff_stream=new ffmpeg.start(wsServer)
74+
75+
// var j_data=JSON.parse(data.toString())
76+
// if(j_data.action=="start_video"){
77+
// console.log("==>starting video")
78+
// ff_stream=new ffmpeg.start(wsServer)
79+
// }//start video
80+
// if(j_data.action=="stop_video"){
81+
// console.log("==>stoping video")
82+
// try{ff_stream.kill()}
83+
// catch(e){console.log(e)}
84+
// }//start video
85+
// })//
86+
87+
// ws.on("close",function(){
88+
// console.log("==>ws client closed: ")
89+
// })//
7090

71-
})//cconected socked
91+
// })//cconected socked
7292

7393
console.log("Server started from smb!")
7494
console.log(netInt)

streamer.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
var spawn = require('child_process').spawn;
2+
const Split = require('stream-split');
3+
var fs=require("fs")
4+
const NALseparator = new Buffer([0,0,0,1]);//NAL break
5+
6+
function start(socket){
7+
console.log("==>>>>>start streaming")
8+
9+
// var proc=spawn("ffmpeg",[
10+
// "-s","640x360",
11+
// "-re",
12+
// "-framerate","30",
13+
// "-pixel_format","yuv420p",//"yuv420p",//yuyv422
14+
// //"-f","rawvideo",
15+
// "-i","/dev/video0",
16+
// // "-c:v","h264_mmal",
17+
// // "-i","/home/pi/360.mp4",
18+
// "-c:v","h264_omx",
19+
// "-b:v","1M",
20+
// "-s","640x360",
21+
// //"-s","1920x1080",
22+
// "-an",
23+
// //"-profile:v","baseline",//baseline
24+
// //"-vf","drawtext='fontfile=/home/pi/ffmpeg/freefont/FreeSans.ttf:text=%{localtime\}':fontsize=50:fontcolor=yellow@1:box=1:[email protected]:x=(w-tw)/2:y=10",
25+
// "-loglevel","error",
26+
// "-stats",
27+
// "-tune","zerolatency",
28+
// "-f","h264",
29+
// //"-reset_timestamps", "1",
30+
// //"-movflags","isml+empty_moov+faststart",//+faststart//"frag_keyframe+empty_moov",
31+
// //"-fflags","nobuffer",
32+
// //"-frag_duration","5",
33+
// "-y",
34+
// //"cam_video.mp4"
35+
// "-"
36+
// ])
37+
var proc = spawn('raspivid', [//work!
38+
'-t', '0',
39+
'-o', '-',
40+
"-n",
41+
'-w', 640,
42+
'-h', 360,
43+
'-fps', 30,
44+
'-pf', "baseline"//'baseline'
45+
]);
46+
47+
var rawstream=proc.stdout.pipe(new Split(NALseparator))
48+
49+
//read data from file
50+
//ffmpeg -i 360.mp4 -c:v h264 -vprofile baseline -b:v 1M -s 640x360 -y baseline.h264
51+
//var readStream=fs.createReadStream("/home/pi/baselinepi2.h264")
52+
//var rawstream=readStream.pipe(new Split(NALseparator))
53+
54+
rawstream.on("data",function(data){
55+
//broadcast(Buffer.concat([NALseparator, data]))
56+
socket.emit("nal_packet",Buffer.concat([NALseparator, data]))
57+
//socket.send(Buffer.concat([NALseparator, data]))
58+
})
59+
60+
61+
// function broadcast(data){
62+
// stream.clients.forEach(function(socket) {
63+
// socket.send(data,{ binary: true})
64+
// })//clients
65+
// }//broadcast
66+
67+
68+
proc.stderr.on("data",function(data){
69+
console.log("==>sdterr: "+data.toString())
70+
})//on error
71+
72+
proc.on("close",function(code){
73+
console.log("process exit with code: "+code)
74+
})//on close
75+
76+
return proc
77+
}//start
78+
79+
exports.start=start

www/broadway/Decoder.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)