Blame view

trunk/research/librtmp/srs_ingest_flv.c 8.1 KB
1 2 3
/*
The MIT License (MIT)
4
Copyright (c) 2013-2015 SRS(simple-rtmp-server)
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
gcc srs_ingest_flv.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_ingest_flv
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "../../objs/include/srs_librtmp.h"
winlin authored
37
int proxy(srs_flv_t flv, srs_rtmp_t ortmp);
38 39
int connect_oc(srs_rtmp_t ortmp);
40
#define RE_PULSE_MS 300
41
int64_t re_create();
42 43
void re_update(int64_t re, int32_t starttime, u_int32_t time);
void re_cleanup(int64_t re, int32_t starttime, u_int32_t time);
44
45
int64_t tools_main_entrance_startup_time;
46 47 48 49
int main(int argc, char** argv)
{
    int ret = 0;
    
50
    // main function
51
    tools_main_entrance_startup_time = srs_utils_time_ms();
52
    
53 54 55
    // user option parse index.
    int opt = 0;
    // user options.
winlin authored
56 57
    char* in_flv_file = NULL;
    char* out_rtmp_url = NULL;
58 59 60
    // rtmp handler
    srs_rtmp_t ortmp;
    // flv handler
winlin authored
61
    srs_flv_t flv;
62
    
63 64 65 66
    printf("ingest flv file and publish to RTMP server like FFMPEG.\n");
    printf("srs(simple-rtmp-server) client librtmp library.\n");
    printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
    
67 68 69 70 71 72
    if (argc <= 2) {
        printf("ingest flv file and publish to RTMP server\n"
            "Usage: %s <-i in_flv_file> <-y out_rtmp_url>\n"
            "   in_flv_file     input flv file, ingest from this file.\n"
            "   out_rtmp_url    output rtmp url, publish to this url.\n"
            "For example:\n"
73 74 75 76
            "   %s -i doc/source.200kbps.768x320.flv -y rtmp://127.0.0.1/live/livestream\n"
            "   %s -i ../../doc/source.200kbps.768x320.flv -y rtmp://127.0.0.1/live/livestream\n",
            argv[0], argv[0], argv[0]);
        exit(-1);
77 78
    }
    
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    for (opt = 0; opt < argc; opt++) {
        srs_human_trace("argv[%d]=%s", opt, argv[opt]);
    }
    
    // fill the options for mac
    for (opt = 0; opt < argc - 1; opt++) {
        // ignore all options except -i and -y.
        char* p = argv[opt];
        
        // only accept -x
        if (p[0] != '-' || p[1] == 0 || p[2] != 0) {
            continue;
        }
        
        // parse according the option name.
        switch (p[1]) {
            case 'i': in_flv_file = argv[opt + 1]; break;
            case 'y': out_rtmp_url = argv[opt + 1]; break;
            default: break;
98 99 100
        }
    }
    
winlin authored
101 102 103 104 105 106 107 108 109
    if (!in_flv_file) {
        srs_human_trace("input invalid, use -i <input>");
        return -1;
    }
    if (!out_rtmp_url) {
        srs_human_trace("output invalid, use -y <output>");
        return -1;
    }
    
110 111
    srs_human_trace("input:  %s", in_flv_file);
    srs_human_trace("output: %s", out_rtmp_url);
112
113
    if ((flv = srs_flv_open_read(in_flv_file)) == NULL) {
114
        ret = 2;
115
        srs_human_trace("open flv file failed. ret=%d", ret);
116 117 118 119 120
        return ret;
    }
    
    ortmp = srs_rtmp_create(out_rtmp_url);
winlin authored
121
    ret = proxy(flv, ortmp);
122
    srs_human_trace("ingest flv to RTMP completed");
123 124
    
    srs_rtmp_destroy(ortmp);
winlin authored
125
    srs_flv_close(flv);
126 127 128 129
    
    return ret;
}
130
int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, u_int32_t* ptimestamp)
131 132 133 134
{
    int ret = 0;
    
    // packet data
winlin authored
135 136
    char type;
    int size;
137 138
    char* data = NULL;
    
139
    srs_human_trace("start ingest flv to RTMP stream");
140
    for (;;) {
winlin authored
141 142 143
        // tag header
        if ((ret = srs_flv_read_tag_header(flv, &type, &size, ptimestamp)) != 0) {
            if (srs_flv_is_eof(ret)) {
144
                srs_human_trace("parse completed.");
winlin authored
145 146
                return 0;
            }
147
            srs_human_trace("flv get packet failed. ret=%d", ret);
winlin authored
148 149 150 151
            return ret;
        }
        
        if (size <= 0) {
152
            srs_human_trace("invalid size=%d", size);
winlin authored
153 154 155 156 157 158
            break;
        }
        
        // TODO: FIXME: mem leak when error.
        data = (char*)malloc(size);
        if ((ret = srs_flv_read_tag_data(flv, data, size)) != 0) {
159 160 161
            return ret;
        }
        
162 163
        u_int32_t timestamp = *ptimestamp;
        
164 165
        if ((ret = srs_human_print_rtmp_packet(type, timestamp, data, size)) != 0) {
            srs_human_trace("print packet failed. ret=%d", ret);
166 167 168
            return ret;
        }
        
169
        if ((ret = srs_rtmp_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) {
170
            srs_human_trace("irtmp get packet failed. ret=%d", ret);
171 172
            return ret;
        }
173
            
174
        if (*pstarttime < 0 && srs_utils_flv_tag_is_av(type)) {
175 176
            *pstarttime = *ptimestamp;
        }
177
        
178
        re_update(re, *pstarttime, *ptimestamp);
179 180 181 182 183
    }
    
    return ret;
}
winlin authored
184
int proxy(srs_flv_t flv, srs_rtmp_t ortmp)
185 186 187
{
    int ret = 0;
    u_int32_t timestamp = 0;
188
    int32_t starttime = -1;
189
    
winlin authored
190 191
    char header[13];
    if ((ret = srs_flv_read_header(flv, header)) != 0) {
192 193 194 195 196 197 198 199
        return ret;
    }
    if ((ret = connect_oc(ortmp)) != 0) {
        return ret;
    }
    
    int64_t re = re_create();
    
200
    ret = do_proxy(flv, ortmp, re, &starttime, &timestamp);
201 202
    
    // for the last pulse, always sleep.
203
    re_cleanup(re, starttime, timestamp);
204 205 206 207
    
    return ret;
}
208 209 210 211
int connect_oc(srs_rtmp_t ortmp)
{
    int ret = 0;
    
212
    if ((ret = srs_rtmp_handshake(ortmp)) != 0) {
213
        srs_human_trace("ortmp simple handshake failed. ret=%d", ret);
214 215
        return ret;
    }
216
    srs_human_trace("ortmp simple handshake success");
217
    
218
    if ((ret = srs_rtmp_connect_app(ortmp)) != 0) {
219
        srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret);
220 221
        return ret;
    }
222
    srs_human_trace("ortmp connect vhost/app success");
223
    
224
    if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) {
225
        srs_human_trace("ortmp publish stream failed. ret=%d", ret);
226 227
        return ret;
    }
228
    srs_human_trace("ortmp publish stream success");
229 230 231 232 233 234
    
    return ret;
}

int64_t re_create()
{
235
    // if not very precise, we can directly use this as re.
236
    int64_t re = srs_utils_time_ms();
237 238 239
    
    // use the starttime to get the deviation
    int64_t deviation = re - tools_main_entrance_startup_time;
240
    srs_human_trace("deviation is %d ms, pulse is %d ms", (int)(deviation), (int)(RE_PULSE_MS));
241
    
242 243 244
    // so, we adjust time to max(0, deviation)
    // because the last pulse, we already sleeped
    int adjust = (int)(deviation);
245
    if (adjust > 0) {
246
        srs_human_trace("adjust re time for %d ms", adjust);
247 248
        re -= adjust;
    } else {
249
        srs_human_trace("no need to adjust re time");
250 251 252 253
    }
    
    return re;
}
254
void re_update(int64_t re, int32_t starttime, u_int32_t time)
255
{
256
    // send by pulse algorithm.
257
    int64_t now = srs_utils_time_ms();
258
    int64_t diff = time - starttime - (now -re);
259
    if (diff > RE_PULSE_MS) {
winlin authored
260
        usleep((useconds_t)(diff * 1000));
261 262
    }
}
263
void re_cleanup(int64_t re, int32_t starttime, u_int32_t time)
264 265 266
{
    // for the last pulse, always sleep.
    // for the virtual live encoder long time publishing.
267
    int64_t now = srs_utils_time_ms();
268
    int64_t diff = time - starttime - (now -re);
269
    if (diff > 0) {
270
        srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms", 
271
            (int)diff, starttime, time);
winlin authored
272
        usleep((useconds_t)(diff * 1000));
273 274
    }
}