Skip to content

Commit

Permalink
Play archived records from the begining
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Kiseliov committed Jun 12, 2013
1 parent 51dcc6c commit d9f76a7
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 70 deletions.
4 changes: 2 additions & 2 deletions cameras.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct motion_detection {
struct in_out_cpy {
AVFormatContext *in_ctx;
AVFormatContext *out_ctx;
int in_stream_index;
AVStream *in_stream;
AVStream *out_stream;
int active;
};
Expand Down Expand Up @@ -78,4 +78,4 @@ struct cam_consumer {
void error(const char *msg);
void av_crit(char *msg, int errnum);
void *recorder_thread(void *ptr);
void init_screen(struct screen *screen);
int init_screen(struct screen *screen);
8 changes: 5 additions & 3 deletions control_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ static void parse_command(int client_fd, int *close_conn) {

printf("session_id: %d, type: %d, ncams: %d, timestamp: %ld\n", session_id, type, ncams, timestamp);

init_screen(screen);

sprintf(buffer, "{ \"session_id\": %d, \"width\": %d, \"height\": %d }\n", screen->session_id, screen->width, screen->height);
if(init_screen(screen) < 0) {
sprintf(buffer, "{ \"error\": \"true\" }\n");
} else {
sprintf(buffer, "{ \"session_id\": %d, \"width\": %d, \"height\": %d }\n", screen->session_id, screen->width, screen->height);
}
snd(client_fd, buffer, close_conn);
} else if(type == STATS) {
struct statvfs statvfs_buf;
Expand Down
37 changes: 27 additions & 10 deletions database.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,11 @@ void update_videofile(struct camera *cam) {
PQclear(result);
}

void find_video_file(int cam_id, char *fname, time_t *timestamp) {
int find_video_file(int cam_id, char *fname, time_t *timestamp) {
char query[256], date_part[32];
time_t start_ts;
int videofile_id;
int found = 0, ret = 0;

snprintf(query, sizeof(query), "SELECT code FROM cameras where id = %d;", cam_id);
PGresult *result1 = exec_query(query);
Expand All @@ -179,29 +180,45 @@ void find_video_file(int cam_id, char *fname, time_t *timestamp) {
exit(EXIT_FAILURE);
}

if(PQntuples(result1) != 1) {
PQclear(result1);
fprintf(stderr, "No camera found\n");
return -1;
}

char *cam_code = PQgetvalue(result1, 0, 0);

snprintf(query, sizeof(query),
"SELECT id, date_part('epoch', started_at) FROM videofiles WHERE camera_id = %d AND started_at <= to_timestamp(%ld) at time zone 'UTC' AND (finished_at <= to_timestamp(%ld) at time zone 'UTC' OR finished_at IS NULL);",
"SELECT id, date_part('epoch', started_at) FROM videofiles WHERE camera_id = %d AND started_at <= to_timestamp(%ld) at time zone 'UTC' AND (finished_at >= to_timestamp(%ld) at time zone 'UTC' OR finished_at IS NULL);",
cam_id, *timestamp, *timestamp);

PGresult *result2 = exec_query(query);
if(PQresultStatus(result2) != PGRES_TUPLES_OK) {
fprintf(stderr, "Insertion failed(videofile): %s\n", PQresultErrorMessage(result2));
fprintf(stderr, "Selection failed(videofile): %s\n", PQresultErrorMessage(result2));
exit(EXIT_FAILURE);
}

char *id = PQgetvalue(result2, 0, 0);
char *ts = PQgetvalue(result2, 0, 1);
found = PQntuples(result2);
if(found == 1) {
char *id = PQgetvalue(result2, 0, 0);
char *ts = PQgetvalue(result2, 0, 1);

sscanf(id, "%d", &videofile_id);
sscanf(ts, "%ld", &start_ts);

sscanf(id, "%d", &videofile_id);
sscanf(ts, "%ld", &start_ts);
*timestamp -= start_ts;

*timestamp -= start_ts;
strftime(date_part, sizeof(date_part), "%Y%m%d/%Y%m%d%H%M%S", localtime(&start_ts));
snprintf(fname, 1024, "%s/%s/%s.h264", store_dir, cam_code, date_part);

strftime(date_part, sizeof(date_part), "%Y%m%d/%Y%m%d%H%M%S", localtime(&start_ts));
snprintf(fname, 1024, "%s/%s/%s.h264", store_dir, cam_code, date_part);
fprintf(stderr, "Found file: %s at %d\n", fname, (int)*timestamp);
ret = 0;
} else {
fprintf(stderr, "Files found: %d\n", found);
ret = -1;
}

PQclear(result1);
PQclear(result2);
return ret;
}
2 changes: 1 addition & 1 deletion database.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ struct camera* select_cameras(int *ncams);
void create_event(int cam_id, time_t raw_started_at, time_t raw_finished_at);
void create_videofile(struct camera *cam, char *filepath);
void update_videofile(struct camera *cam);
void find_video_file(int cam_id, char *fname, time_t *timestamp);
int find_video_file(int cam_id, char *fname, time_t *timestamp);
10 changes: 6 additions & 4 deletions recorder_thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ void *recorder_thread(void *ptr) {

cnt = (cnt + 1) % cam->analize_frames;
if(cnt == 0) {
if((ret = avcodec_decode_video2(cam->codec, frame, &got_frame, &packet)) < 0) {
if((ret = avcodec_decode_video2(cam->codec, frame, &got_frame, &packet)) < 0)
av_crit("avcodec_decode_video2", ret);
}

if(got_frame) {
if(detect_motion(&md, frame)) {
if(first_activity == 0) first_activity = time(NULL);
Expand All @@ -217,13 +217,15 @@ void *recorder_thread(void *ptr) {
}

packet.stream_index = cam->output_stream->id;
av_write_frame(cam->output_context, &packet);
if((ret = av_write_frame(cam->output_context, &packet)) < 0)
av_crit("av_write_frame", ret);

for(l1 *p = cam->cam_consumers_list; p != NULL; p = p->next) {
struct cam_consumer *consumer = (struct cam_consumer *)p->value;
if(consumer->screen->ncams == 1) {
packet.stream_index = consumer->screen->rtp_stream->id;
av_write_frame(consumer->screen->rtp_context, &packet);
if((ret = av_write_frame(consumer->screen->rtp_context, &packet)) < 0)
av_crit("av_write_frame", ret);
} else {
// decode frame
// rescame image
Expand Down
63 changes: 47 additions & 16 deletions screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ unsigned int ports = 0;

extern int should_terminate;

static void open_video_file(struct screen *screen);
static int open_video_file(struct screen *screen);
static void init_single_camera_screen(struct screen *screen);
static void init_multiple_camera_screen(struct screen *screen);

void init_screen(struct screen *screen) {
int init_screen(struct screen *screen) {
char gst_pipe[128], path[128];
int port;
unsigned int i;
Expand Down Expand Up @@ -39,7 +39,8 @@ void init_screen(struct screen *screen) {
l1_insert(&screen->cams[i]->cam_consumers_list, consumer);
}
} else {
open_video_file(screen);
if(open_video_file(screen) < 0)
return -1;
}

sprintf(gst_pipe, "( filesrc location=/tmp/stream_%d.sdp ! sdpdemux name=dynpay0 )", screen->rtp_port);
Expand All @@ -51,6 +52,7 @@ void init_screen(struct screen *screen) {
gst_rtsp_media_mapping_add_factory(mapping, path, factory);
gst_rtsp_media_factory_set_shared(factory, TRUE);
g_object_unref(mapping);
return 0;
}

static void create_sdp(struct screen *screen) {
Expand Down Expand Up @@ -153,13 +155,32 @@ static void init_multiple_camera_screen(struct screen *screen) {
}

static void* copy_input_to_output(void *ptr) {
int ret;
struct in_out_cpy *io = (struct in_out_cpy *)ptr;
AVPacket packet;

int frame = 0;
int64_t start = av_gettime();
int64_t pts = 0, now;

AVRational frame_rate = io->in_stream->r_frame_rate;

av_init_packet(&packet);
while(av_read_frame(io->in_ctx, &packet) >= 0) {
while(1) {
ret = av_read_frame(io->in_ctx, &packet);
if(ret == AVERROR_EOF) {
printf("EOF\n");
break;
}
if(ret < 0) {
av_crit("av_read_frame", ret);
}

packet.stream_index = io->out_stream->id;
av_write_frame(io->out_ctx, &packet);
packet.dts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, io->out_stream->time_base);

if((ret = av_write_frame(io->out_ctx, &packet)) < 0)
av_crit("av_write_frame", ret);

av_free_packet(&packet);
av_init_packet(&packet);
Expand All @@ -168,23 +189,33 @@ static void* copy_input_to_output(void *ptr) {
break;
if(should_terminate)
break;

frame += 1;
pts = frame * frame_rate.den * AV_TIME_BASE / frame_rate.num;
now = av_gettime() - start;

if(pts > now) {
av_usleep(pts - now);
}
}

//avcodec_close(io->out_stream->codec);
//avcodec_close(io->in_ctx->streams[io->in_stream_index]->codec);
avio_close(io->out_ctx->pb);
avformat_close_input(&io->in_ctx);
avformat_free_context(io->out_ctx);
free(io);
return NULL;
}

static void open_video_file(struct screen *screen) {
static int open_video_file(struct screen *screen) {
int ret;
struct camera *cam = screen->cams[0];
AVFormatContext *s = avformat_alloc_context();
find_video_file(cam->id, s->filename, &screen->timestamp);

if(find_video_file(cam->id, s->filename, &screen->timestamp) < 0) {
avformat_free_context(s);
return -1;
}

if((ret = avformat_open_input(&s, s->filename, NULL, NULL)) < 0)
av_crit("avformat_open_input", ret);
if((ret = avformat_find_stream_info(s, NULL)) < 0)
Expand All @@ -195,13 +226,10 @@ static void open_video_file(struct screen *screen) {

AVStream *input_stream = s->streams[video_stream_index];
AVCodecContext *codec = input_stream->codec;
input_stream->time_base = codec->time_base = AV_TIME_BASE_Q;
input_stream->r_frame_rate = cam->input_stream->r_frame_rate;

input_stream->time_base.den = codec->time_base.den = cam->input_stream->codec->time_base.den;
input_stream->time_base.num = codec->time_base.num = cam->input_stream->codec->time_base.num;

if((ret = av_seek_frame(s, video_stream_index, av_rescale_q((int64_t)screen->timestamp, AV_TIME_BASE_Q, input_stream->time_base), 0)) < 0)
av_crit("av_seek_frame", ret);

// TODO: here must be seek

AVFormatContext *rtp_context;
AVOutputFormat *rtp_fmt;
Expand All @@ -226,7 +254,7 @@ static void open_video_file(struct screen *screen) {
struct in_out_cpy *io = (struct in_out_cpy*)malloc(sizeof(struct in_out_cpy));
io->in_ctx = s;
io->out_ctx = rtp_context;
io->in_stream_index = video_stream_index;
io->in_stream = input_stream;
io->out_stream = rtp_stream;
io->active = 1;

Expand All @@ -241,6 +269,9 @@ static void open_video_file(struct screen *screen) {
screen->width = cam->codec->width;
screen->height = cam->codec->height;
screen->io = io;

create_sdp(screen);
return 0;
}

static gboolean timeout(GstRTSPServer *server, gboolean ignored) {
Expand Down
2 changes: 1 addition & 1 deletion server.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void error(const char *msg) {

void av_crit(char *msg, int errnum) {
char errbuf[2048];
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "%s errnum: %d\n", msg, errnum);
if(errnum != 0 && av_strerror(errnum, errbuf, sizeof(errbuf)) == 0) {
fprintf(stderr, "%s\n", errbuf);
}
Expand Down
33 changes: 30 additions & 3 deletions webpanel-rails/app/assets/javascripts/cameras.js.coffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
jQuery ->
$session_id = undefined
$vlc = document.getElementById("vlc")

check_cam_cnt = ->
if $('.camera.selected').length > 0
Expand All @@ -13,6 +14,21 @@ jQuery ->
else
n.toString()

load_events = ->
$.ajax {
url: $('.timeline').data('events-url'),
type: "GET",
dataType: 'json',
data: { date: $('.timeline').data('date') },
success: (data) ->
$('.timeline .videofile, .timeline .event').remove()
$.each data.entries, (i, entry) ->
$('<div>').attr('class', entry.entry_type)
.data('info', {starts_at: entry.started_at, finished_at: entry.finished_at})
.css({width: entry.rel_width, marginLeft: entry.rel_margin})
.appendTo('.timeline');
}

$('.cameras.selectable .camera-btn').click ->
cam = $(this).closest('.camera')
if cam.hasClass('selected')
Expand Down Expand Up @@ -47,6 +63,17 @@ jQuery ->
$.post($('.cameras').data('sort-url'), { camera_id: ui.item.data('camera-id'), position: ui.item.index() })
});

$('.datepicker').on 'changeDate', (ev)->
dd = _02d(ev.date.getDate())
mm = _02d(ev.date.getMonth()+1)
yyyy = ev.date.getFullYear()
$('.timeline').data('date', "#{yyyy}-#{mm}-#{dd}")
$('#date').text("#{dd}.#{mm}.#{yyyy}")
load_events()

if $('.timeline').length
load_events()

$('.timeline').on 'mousemove', (e) ->
top = $(this).position().top
posX = $(this).parent().position().left
Expand All @@ -69,9 +96,9 @@ jQuery ->
dataType: 'json',
data: { date: that.data('date'), position: that.data('position'), session_id: $session_id },
success: (data)->
if data.url
id = @vlc.playlist.add(data.url, "");
@vlc.playlist.playItem(id);
if data.rtsp
id = $vlc.playlist.add(data.rtsp, "");
$vlc.playlist.playItem(id);
if data.session_id
$session_id = data.session_id;
}
Expand Down
10 changes: 6 additions & 4 deletions webpanel-rails/app/controllers/cameras_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def timeline_entries
day_len = 24*60*60.0
entries = (camera.events.where("started_at >= ? AND finished_at < ?", date, date+1.day).all +
camera.videofiles.where("started_at >= ? AND (finished_at < ? OR finished_at IS NULL)", date, date+1.day).all).map do |entry|
next if entry.started_at.day != date.day
entry.finished_at = Time.now if entry.is_a?(Videofile) && entry.finished_at.nil?

rel_margin = ((entry.started_at.to_i - date.to_i) / day_len).round(4)*100
Expand All @@ -23,7 +24,7 @@ def timeline_entries
rel_width: "#{rel_width}%"
}
end
render json: { entries: entries }
render json: { entries: entries.compact }
end

def seek
Expand All @@ -33,15 +34,16 @@ def seek
session_id = params[:session_id].try(:to_i)
date += position.seconds

VIDEO_SERVER
session_id = VIDEO_SERVER.show_archive(camera.id, date, session_id)
render json: { rtsp: "rtsp://#{request.host}:8554/stream_#{session_id}", session_id: session_id }
end

def watch
template = params[:template]
cameras = Camera.find(params[:ids].split(','))

session_id = VIDEO_SERVER.show_real_image(cameras)
@rtsp_link = "rtsp://#{request.host}:8554/stream_#{session_id}"
@session_id = VIDEO_SERVER.show_real_image(cameras)
@rtsp_link = "rtsp://#{request.host}:8554/stream_#{@session_id}"
end

def select
Expand Down
9 changes: 5 additions & 4 deletions webpanel-rails/app/models/video_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ def initialize
connect
end

def show_real_image(cameras)
result = send type: 'real', cam_ids: cameras.map(&:id)
def show_archive(camera_id, timestamp, session_id=nil)
result = send type: 'archive', cam_ids: [camera_id], timestamp: timestamp.to_i, session_id: session_id
result['session_id']
end

def show_archive(session_id, camera_id, timestampt)
send type: 'real', cam_ids: cameras.map(&:id)
def show_real_image(cameras)
result = send type: 'real', cam_ids: cameras.map(&:id)
result['session_id']
end

def stats
Expand Down
Loading

0 comments on commit d9f76a7

Please sign in to comment.