~polto/elphelogm/trunk

« back to all changes in this revision

Viewing changes to dumper.cpp

  • Committer: Alexandre Poltorak
  • Date: 2008-03-16 15:36:14 UTC
  • Revision ID: polto@alsenet.com-20080316153614-hh918wm42mgrvrdy
my first bazaar commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.                                           *
 
9
 *                                                                               *
 
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.                                  *
 
14
 *                                                                               *
 
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
 *********************************************************************************
 
19
 *
 
20
 * $Id: dumper.c,v 1.21 2006/09/03 11:50:44 khlut Exp $
 
21
 *
 
22
 * $Log: dumper.c,v $
 
23
 * Revision 1.21  2006/09/03 11:50:44  khlut
 
24
 * bug fixed: drops all frames
 
25
 *
 
26
 * Revision 1.20  2006/08/30 18:11:49  khlut
 
27
 * new substitution $t in file template
 
28
 *
 
29
 * Revision 1.19  2006/08/30 17:44:34  khlut
 
30
 * raw jpeg output
 
31
 *
 
32
 * Revision 1.18  2006/08/30 17:08:34  khlut
 
33
 * skip old frames
 
34
 *
 
35
 * Revision 1.17  2006/08/30 17:02:31  khlut
 
36
 * message levels added
 
37
 *
 
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
 
41
 *
 
42
 * Revision 1.15  2006/03/19 15:13:33  khlut
 
43
 * interactive mode - to minimize latency
 
44
 *
 
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
 
48
 *
 
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.
 
53
 *
 
54
 * Revision 1.12  2006/03/07 05:56:08  khlut
 
55
 * version 0.9.3
 
56
 * Statistics output now one time per second
 
57
 *
 
58
 * Revision 1.11  2006/03/04 07:19:40  khlut
 
59
 * all frames are syncpoints in mjpeg
 
60
 *
 
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
 
64
 *
 
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
 
68
 *
 
69
 * Revision 1.8  2006/02/06 16:56:29  khlut
 
70
 * version 0.9.1
 
71
 * fixed bug with zero ogg packet fragments
 
72
 * added printing for output filename
 
73
 * sss: ----------------------------------------------------------------------
 
74
 *
 
75
 * Revision 1.7  2006/01/30 12:15:42  khlut
 
76
 * losted changes added
 
77
 *
 
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
 
81
 *
 
82
 * Revision 1.5  2005/12/25 18:17:41  landy_
 
83
 * sched_yeld replaced by usleep when starting(increase CPU overload)
 
84
 *
 
85
 * Revision 1.4  2005/12/22 11:19:28  landy_
 
86
 * Added automaticaly defines FPS of stream. Patched buffer overflows
 
87
 *
 
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
 
91
 *
 
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
 
94
 * and overlay it
 
95
 *
 
96
 * Revision 1.1.1.1  2005/12/13 17:06:39  landy_
 
97
 * Created directory structure and import sources
 
98
 *
 
99
 *******************************************************************************/
 
100
#include "defines.h"
 
101
#include "ogmstreams.h"
 
102
 
 
103
// Dump one frame if it ready
 
104
 
 
105
int             header_produced = 0;
 
106
int             snum = 0;
 
107
uint32_t        prevts;
 
108
int             notfirst = 0;
 
109
long long       frames;
 
110
int             rawmode = 0;
 
111
 
 
112
void write_ogg_packet(param_t * p, int bytes, ogg_int64_t granulepos, int last)
 
113
{
 
114
        if(rawmode) {
 
115
                if(bytes < 2 || !granulepos)
 
116
                        return;
 
117
                if(fwrite(p->buffer + 1, 1, bytes - 1, p->fd) != bytes - 1) {
 
118
                        perror("\nfwrite");
 
119
                        exit(3);
 
120
                }
 
121
        } else {
 
122
                ogg_packet      header;
 
123
 
 
124
                header.packet = p->buffer;
 
125
                header.bytes = bytes;
 
126
                header.b_o_s = p->packetno == 0;
 
127
                header.e_o_s = last;
 
128
                header.packetno = p->packetno++;;
 
129
                header.granulepos = granulepos;
 
130
                ogg_stream_packetin(&(p->os), &header);
 
131
 
 
132
                while((last || p->imode && granulepos ? ogg_stream_flush : ogg_stream_pageout) (&(p->os), &(p->og))) {
 
133
                        int             i,
 
134
                                        j;
 
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) {
 
136
                                //j=errno;
 
137
                                //fprintf(stderr,"\n%d %d %d\n",i,p->og.header_len,p->og.body_len);
 
138
                                //errno=j;
 
139
                                perror("\nfwrite");
 
140
                                exit(3);
 
141
                        }
 
142
                }
 
143
        }
 
144
        if(p->imode && granulepos)
 
145
                fflush(p->fd);
 
146
}
 
147
 
 
148
void           *dump(param_t * p, int last)
 
149
{
 
150
        int             len = 0;
 
151
        uint8_t        *tempbuf;
 
152
        uint32_t        ts;
 
153
        stream_header   sh;
 
154
        vorbis_comment *comment;
 
155
        time_t          t = time(0);
 
156
 
 
157
        tempbuf = p->buffer;
 
158
        if(p->start_time == 0)
 
159
                p->start_time = t;
 
160
 
 
161
        if(p->ftmpl && (!p->fname || !p->fd)) {
 
162
                char           *newname = (char *) malloc(10240);
 
163
                if(!newname) {
 
164
                        perror("malloc");
 
165
                        exit(2);
 
166
                }
 
167
                strftime(newname, 10240, p->ftmpl, localtime(&t));
 
168
                if(p->fd)
 
169
                        fclose(p->fd);
 
170
                p->fd = fopen(newname, "w");
 
171
                if(!p->fd) {
 
172
                        perror(newname);
 
173
                        exit(1);
 
174
                }
 
175
                msg(1, "\nSaving to file:%s\n", newname);
 
176
                header_produced = 0;
 
177
                p->cur_frames = 0;
 
178
                p->start_time = t;
 
179
                if(p->fname)
 
180
                        free(p->fname);
 
181
                p->fname = newname;
 
182
        }
 
183
 
 
184
        if(header_produced && (p->width != peek_size(&(p->rtp_queue), 0) || p->height != peek_size(&(p->rtp_queue), 1)))
 
185
                header_produced = 2;
 
186
 
 
187
        if(header_produced != 1) {
 
188
                if(!header_produced) {
 
189
                        ogg_stream_init(&(p->os), p->serialno);
 
190
                        p->packetno = 0;
 
191
                        if(!rawmode)
 
192
                                p->last_granulepos = 0;
 
193
                }
 
194
                header_produced = 1;
 
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);
 
197
                }
 
198
                // Write out the first page of stream
 
199
                // because header must come together before any
 
200
                // non-header pages.
 
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));
 
215
 
 
216
                // put it into Ogg stream
 
217
                write_ogg_packet(p, 1 + get_uint32(&sh.size), 0, 0);
 
218
 
 
219
                // make comments headers
 
220
                comment = generate_vorbis_comment();
 
221
                len = comments_to_buffer(comment, (char *)tempbuf, p->max_frame_size + 1);
 
222
                if(len < 0) {
 
223
                        msg(0, "error: buffer is too small for %d bytes " "(can hold %ld bytes).\n", len, p->max_frame_size + 1);
 
224
                        return 0;
 
225
                }
 
226
                if(comment != NULL) {
 
227
                        vorbis_comment_clear(comment);
 
228
                        free(comment);
 
229
                }
 
230
 
 
231
                write_ogg_packet(p, len, 0, 0);
 
232
 
 
233
                // we are ready dumping stream data
 
234
        }                                                       // if header_produced
 
235
 
 
236
        len = get_frame(&(p->rtp_queue), &tempbuf[1], (int32_t *)&ts);  // get jpeg picture from RTP buffer
 
237
 
 
238
        if(len == 0)
 
239
                return 0;                               // no frame
 
240
        if(notfirst && (signed) ts - (signed) prevts < 0) {
 
241
                msg(2, "\nskip old:%u (was:%u)\n", ts, prevts);
 
242
                return 0;
 
243
        }
 
244
        if(snum > 0) {
 
245
                msg(2, "\nskip:%u\n", ts);
 
246
                snum--;
 
247
                return 0;
 
248
        }
 
249
        msg(2, "\nout:%u\n", ts);
 
250
        snum = p->snum;
 
251
        if(notfirst && p->delta_ts != ts - prevts) {
 
252
                //header_produced=2;
 
253
                p->delta_ts = ts - prevts;
 
254
        } else
 
255
                notfirst = 1;
 
256
        prevts = ts;
 
257
 
 
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))
 
260
                last = 1;
 
261
 
 
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);
 
263
 
 
264
        p->cur_frames++;
 
265
        frames++;
 
266
 
 
267
        //  fprintf(stderr,"frame: %07lld fps: %.5f filesize:%d \r",frames,
 
268
        //      (double)VIDEO_TIMESCALE/p->delta_ts, ftell(p->fd));
 
269
 
 
270
        if(last && p->fd) {
 
271
                fclose(p->fd);
 
272
                p->fd = 0;
 
273
                if(p->fname) {
 
274
                        char *newname = (char *)malloc(10260);
 
275
                        int ren = 0, i, j;
 
276
                        if(!newname) {
 
277
                                perror("error");
 
278
                                exit(1);
 
279
                        }
 
280
                        for(i = j = 0; j < 10259 && p->fname[i];) {
 
281
                                if(p->fname[i] == '%' && p->fname[i + 1] == '$') {
 
282
                                        ren = 1;
 
283
                                        newname[j++] = p->fname[i++];
 
284
                                        newname[j++] = '%';
 
285
                                        i++;
 
286
                                } else if(p->fname[i] != '$')
 
287
                                        newname[j++] = p->fname[i++];
 
288
                                else {
 
289
                                        ren = 1;
 
290
                                        switch (p->fname[++i]) {
 
291
                                        default:
 
292
                                                newname[j++] = '%';
 
293
                                                continue;
 
294
                                        case '$':
 
295
                                                newname[j++] = '$';
 
296
                                                i++;
 
297
                                                continue;
 
298
                                        case 'n':
 
299
                                                i++;
 
300
                                                j += snprintf(newname + j, 10260 - j, "%d", p->cur_frames);
 
301
                                                continue;
 
302
                                        case 't':
 
303
                                                i++;
 
304
                                                j += snprintf(newname + j, 10260 - j, "%014llu", p->last_granulepos - 1);
 
305
                                                continue;
 
306
                                        }
 
307
                                }
 
308
                        }
 
309
                        if(ren) {
 
310
                                char *newname2 = (char *)malloc(10260);
 
311
                                newname[j] = 0;
 
312
                                if(!newname2) {
 
313
                                        perror("error");
 
314
                                        exit(1);
 
315
                                }
 
316
                                strftime(newname2, 10260, newname, localtime(&t));
 
317
                                if(rename(p->fname, newname2) < 0) {
 
318
                                        perror("file rename error");
 
319
                                        exit(1);
 
320
                                }
 
321
                                msg(1, "\nFile renamed:%s\n", newname2);
 
322
                                free(newname2);
 
323
                        }
 
324
                        free(newname);
 
325
                }
 
326
        }
 
327
 
 
328
        return NULL;
 
329
}