1
/*********************************************************************************
2
* Copyright (C) Elphel , Inc 2005. All Rights Reserved. *
3
* Andrey Latin, mail to: andrey.latin@gmail.com andrey.latin@elphel.com *
4
*********************************************************************************
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the Free Software *
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
18
*********************************************************************************
20
* $Id: dumper.c,v 1.21 2006/09/03 11:50:44 khlut Exp $
23
* Revision 1.21 2006/09/03 11:50:44 khlut
24
* bug fixed: drops all frames
26
* Revision 1.20 2006/08/30 18:11:49 khlut
27
* new substitution $t in file template
29
* Revision 1.19 2006/08/30 17:44:34 khlut
32
* Revision 1.18 2006/08/30 17:08:34 khlut
35
* Revision 1.17 2006/08/30 17:02:31 khlut
36
* message levels added
38
* Revision 1.16 2006/03/21 12:53:48 khlut
39
* Corrected condition of frame is full - added first fragment offset == 0
40
* For compatibility with mplayer in interactive mode - not flush output before first frame
42
* Revision 1.15 2006/03/19 15:13:33 khlut
43
* interactive mode - to minimize latency
45
* Revision 1.14 2006/03/07 19:48:29 khlut
46
* fps=0 now is autodetection by first two frames
47
* fps=-1 mean use variable framerate
49
* Revision 1.13 2006/03/07 18:06:29 khlut
50
* Format for template of file name is extended: $... treated as %... but at the end of file
51
* $n is number of frames %% or %$ - is % and $$ is $
52
* The file is renamed at the end of recording if the name contains new syntax.
54
* Revision 1.12 2006/03/07 05:56:08 khlut
56
* Statistics output now one time per second
58
* Revision 1.11 2006/03/04 07:19:40 khlut
59
* all frames are syncpoints in mjpeg
61
* Revision 1.10 2006/02/28 18:12:17 khlut
62
* added EOS in the last frame
63
* ogg stream flushed only at the end
65
* Revision 1.9 2006/02/20 21:10:29 khlut
66
* a bug with receiving packets for several multiast adresses is fixed
67
* default frame size is 2M
69
* Revision 1.8 2006/02/06 16:56:29 khlut
71
* fixed bug with zero ogg packet fragments
72
* added printing for output filename
73
* sss: ----------------------------------------------------------------------
75
* Revision 1.7 2006/01/30 12:15:42 khlut
76
* losted changes added
78
* Revision 1.6 2006/01/28 21:31:54 khlut
79
* Threads are deleted, poll() used instead
80
* Support for variable fps is added
82
* Revision 1.5 2005/12/25 18:17:41 landy_
83
* sched_yeld replaced by usleep when starting(increase CPU overload)
85
* Revision 1.4 2005/12/22 11:19:28 landy_
86
* Added automaticaly defines FPS of stream. Patched buffer overflows
88
* Revision 1.3 2005/12/20 17:13:26 landy_
89
* Added new option:-k for skipping frames in out stream and made some changes for
90
* formatting output info
92
* Revision 1.2 2005/12/18 18:39:53 landy_
93
* added option -m <file size Mb> and -n <file name> for multiplay files dumping
96
* Revision 1.1.1.1 2005/12/13 17:06:39 landy_
97
* Created directory structure and import sources
99
*******************************************************************************/
101
#include "ogmstreams.h"
103
// Dump one frame if it ready
105
int header_produced = 0;
112
void write_ogg_packet(param_t * p, int bytes, ogg_int64_t granulepos, int last)
115
if(bytes < 2 || !granulepos)
117
if(fwrite(p->buffer + 1, 1, bytes - 1, p->fd) != bytes - 1) {
124
header.packet = p->buffer;
125
header.bytes = bytes;
126
header.b_o_s = p->packetno == 0;
128
header.packetno = p->packetno++;;
129
header.granulepos = granulepos;
130
ogg_stream_packetin(&(p->os), &header);
132
while((last || p->imode && granulepos ? ogg_stream_flush : ogg_stream_pageout) (&(p->os), &(p->og))) {
135
if((i = fwrite(p->og.header, 1, p->og.header_len, p->fd)) != p->og.header_len || p->og.body_len && (i = fwrite(p->og.body, 1, p->og.body_len, p->fd)) != p->og.body_len) {
137
//fprintf(stderr,"\n%d %d %d\n",i,p->og.header_len,p->og.body_len);
144
if(p->imode && granulepos)
148
void *dump(param_t * p, int last)
154
vorbis_comment *comment;
158
if(p->start_time == 0)
161
if(p->ftmpl && (!p->fname || !p->fd)) {
162
char *newname = (char *) malloc(10240);
167
strftime(newname, 10240, p->ftmpl, localtime(&t));
170
p->fd = fopen(newname, "w");
175
msg(1, "\nSaving to file:%s\n", newname);
184
if(header_produced && (p->width != peek_size(&(p->rtp_queue), 0) || p->height != peek_size(&(p->rtp_queue), 1)))
187
if(header_produced != 1) {
188
if(!header_produced) {
189
ogg_stream_init(&(p->os), p->serialno);
192
p->last_granulepos = 0;
195
if(p->fps == 0.0 && p->rtp_queue.qframes > 1) {
196
p->fps = (double) VIDEO_TIMESCALE / (p->rtp_queue.head_f->next->timestamp - p->rtp_queue.head_f->timestamp);
198
// Write out the first page of stream
199
// because header must come together before any
201
*((unsigned char *) tempbuf) = PACKET_TYPE_HEADER;
202
memset(&sh, 0, sizeof(stream_header));
203
strcpy(sh.streamtype, "video");
204
memcpy(sh.subtype, p->codec, 4);
205
put_uint32(&sh.size, sizeof(sh));
206
put_uint64(&sh.time_unit, p->fps > 0.0 ? (ogg_int64_t) (10000000.0 / p->fps)
207
: (ogg_int64_t) 10000000 * VTSD / VIDEO_TIMESCALE);
208
put_uint64(&sh.samples_per_unit, 1);
209
put_uint32(&sh.default_len, 1);
210
put_uint32(&sh.buffersize, p->max_frame_size);
211
put_uint16(&sh.bits_per_sample, p->bpp);
212
put_uint32(&sh.sh.video.width, p->width = peek_size(&(p->rtp_queue), 0));
213
put_uint32(&sh.sh.video.height, p->height = peek_size(&(p->rtp_queue), 1));
214
memcpy(&tempbuf[1], &sh, sizeof(stream_header));
216
// put it into Ogg stream
217
write_ogg_packet(p, 1 + get_uint32(&sh.size), 0, 0);
219
// make comments headers
220
comment = generate_vorbis_comment();
221
len = comments_to_buffer(comment, (char *)tempbuf, p->max_frame_size + 1);
223
msg(0, "error: buffer is too small for %d bytes " "(can hold %ld bytes).\n", len, p->max_frame_size + 1);
226
if(comment != NULL) {
227
vorbis_comment_clear(comment);
231
write_ogg_packet(p, len, 0, 0);
233
// we are ready dumping stream data
234
} // if header_produced
236
len = get_frame(&(p->rtp_queue), &tempbuf[1], (int32_t *)&ts); // get jpeg picture from RTP buffer
239
return 0; // no frame
240
if(notfirst && (signed) ts - (signed) prevts < 0) {
241
msg(2, "\nskip old:%u (was:%u)\n", ts, prevts);
245
msg(2, "\nskip:%u\n", ts);
249
msg(2, "\nout:%u\n", ts);
251
if(notfirst && p->delta_ts != ts - prevts) {
253
p->delta_ts = ts - prevts;
258
*((unsigned char *) tempbuf) = PACKET_IS_SYNCPOINT;
259
if(p->ftmpl && (p->max_size && ftell(p->fd) + len + 45 >= p->max_size || p->max_frames && p->cur_frames + 1 >= p->max_frames || p->max_time && t - p->start_time >= p->max_time))
262
write_ogg_packet(p, len + 1, p->last_granulepos += p->imode && p->rtp_queue.nb_frames > 0 ? 1 : (p->fps > 0.0 ? 1 + (p->delta_ts * p->fps - 1) / VIDEO_TIMESCALE : p->delta_ts / VTSD), last);
267
// fprintf(stderr,"frame: %07lld fps: %.5f filesize:%d \r",frames,
268
// (double)VIDEO_TIMESCALE/p->delta_ts, ftell(p->fd));
274
char *newname = (char *)malloc(10260);
280
for(i = j = 0; j < 10259 && p->fname[i];) {
281
if(p->fname[i] == '%' && p->fname[i + 1] == '$') {
283
newname[j++] = p->fname[i++];
286
} else if(p->fname[i] != '$')
287
newname[j++] = p->fname[i++];
290
switch (p->fname[++i]) {
300
j += snprintf(newname + j, 10260 - j, "%d", p->cur_frames);
304
j += snprintf(newname + j, 10260 - j, "%014llu", p->last_granulepos - 1);
310
char *newname2 = (char *)malloc(10260);
316
strftime(newname2, 10260, newname, localtime(&t));
317
if(rename(p->fname, newname2) < 0) {
318
perror("file rename error");
321
msg(1, "\nFile renamed:%s\n", newname2);