正在显示
5 个修改的文件
包含
310 行增加
和
82 行删除
| @@ -254,6 +254,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -254,6 +254,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 254 | #define ERROR_HTTP_RESPONSE_EOF 4025 | 254 | #define ERROR_HTTP_RESPONSE_EOF 4025 |
| 255 | #define ERROR_HTTP_INVALID_CHUNK_HEADER 4026 | 255 | #define ERROR_HTTP_INVALID_CHUNK_HEADER 4026 |
| 256 | #define ERROR_AVC_NALU_UEV 4027 | 256 | #define ERROR_AVC_NALU_UEV 4027 |
| 257 | +#define ERROR_AAC_BYTES_INVALID 4028 | ||
| 257 | 258 | ||
| 258 | /////////////////////////////////////////////////////// | 259 | /////////////////////////////////////////////////////// |
| 259 | // user-define error. | 260 | // user-define error. |
| @@ -278,6 +278,11 @@ bool srs_string_starts_with(string str, string flag) | @@ -278,6 +278,11 @@ bool srs_string_starts_with(string str, string flag) | ||
| 278 | return str.find(flag) == 0; | 278 | return str.find(flag) == 0; |
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | +bool srs_string_contains(string str, string flag) | ||
| 282 | +{ | ||
| 283 | + return str.find(flag) != string::npos; | ||
| 284 | +} | ||
| 285 | + | ||
| 281 | int srs_do_create_dir_recursively(string dir) | 286 | int srs_do_create_dir_recursively(string dir) |
| 282 | { | 287 | { |
| 283 | int ret = ERROR_SUCCESS; | 288 | int ret = ERROR_SUCCESS; |
| @@ -356,6 +361,22 @@ string srs_path_dirname(string path) | @@ -356,6 +361,22 @@ string srs_path_dirname(string path) | ||
| 356 | return dirname; | 361 | return dirname; |
| 357 | } | 362 | } |
| 358 | 363 | ||
| 364 | +string srs_path_basename(string path) | ||
| 365 | +{ | ||
| 366 | + std::string dirname = path; | ||
| 367 | + size_t pos = string::npos; | ||
| 368 | + | ||
| 369 | + if ((pos = dirname.rfind("/")) != string::npos) { | ||
| 370 | + // the basename("/") is "/" | ||
| 371 | + if (dirname.length() == 1) { | ||
| 372 | + return dirname; | ||
| 373 | + } | ||
| 374 | + dirname = dirname.substr(pos + 1); | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + return dirname; | ||
| 378 | +} | ||
| 379 | + | ||
| 359 | bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) | 380 | bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) |
| 360 | { | 381 | { |
| 361 | char* bytes = stream->data() + stream->pos(); | 382 | char* bytes = stream->data() + stream->pos(); |
| @@ -67,6 +67,8 @@ extern std::string srs_string_remove(std::string str, std::string remove_chars); | @@ -67,6 +67,8 @@ extern std::string srs_string_remove(std::string str, std::string remove_chars); | ||
| 67 | extern bool srs_string_ends_with(std::string str, std::string flag); | 67 | extern bool srs_string_ends_with(std::string str, std::string flag); |
| 68 | // whether string starts with | 68 | // whether string starts with |
| 69 | extern bool srs_string_starts_with(std::string str, std::string flag); | 69 | extern bool srs_string_starts_with(std::string str, std::string flag); |
| 70 | +// whether string contains with | ||
| 71 | +extern bool srs_string_contains(std::string str, std::string flag); | ||
| 70 | 72 | ||
| 71 | // create dir recursively | 73 | // create dir recursively |
| 72 | extern int srs_create_dir_recursively(std::string dir); | 74 | extern int srs_create_dir_recursively(std::string dir); |
| @@ -75,6 +77,8 @@ extern int srs_create_dir_recursively(std::string dir); | @@ -75,6 +77,8 @@ extern int srs_create_dir_recursively(std::string dir); | ||
| 75 | extern bool srs_path_exists(std::string path); | 77 | extern bool srs_path_exists(std::string path); |
| 76 | // get the dirname of path | 78 | // get the dirname of path |
| 77 | extern std::string srs_path_dirname(std::string path); | 79 | extern std::string srs_path_dirname(std::string path); |
| 80 | +// get the basename of path | ||
| 81 | +extern std::string srs_path_basename(std::string path); | ||
| 78 | 82 | ||
| 79 | /** | 83 | /** |
| 80 | * whether stream starts with the avc NALU in "AnnexB" | 84 | * whether stream starts with the avc NALU in "AnnexB" |
| @@ -128,6 +128,16 @@ int main(int argc, char** argv) | @@ -128,6 +128,16 @@ int main(int argc, char** argv) | ||
| 128 | return proxy_hls2rtmp(in_hls_url, out_rtmp_url); | 128 | return proxy_hls2rtmp(in_hls_url, out_rtmp_url); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | +class ISrsAacHandler | ||
| 132 | +{ | ||
| 133 | +public: | ||
| 134 | + /** | ||
| 135 | + * handle the aac frame, which in ADTS format(starts with FFFx). | ||
| 136 | + * @param duration the duration in seconds of frames. | ||
| 137 | +*/ | ||
| 138 | +virtual int on_aac_frame(char* frame, int frame_size, double duration) = 0; | ||
| 139 | +}; | ||
| 140 | + | ||
| 131 | // the context to ingest hls stream. | 141 | // the context to ingest hls stream. |
| 132 | class SrsIngestSrsInput | 142 | class SrsIngestSrsInput |
| 133 | { | 143 | { |
| @@ -185,9 +195,18 @@ public: | @@ -185,9 +195,18 @@ public: | ||
| 185 | /** | 195 | /** |
| 186 | * parse the ts and use hanler to process the message. | 196 | * parse the ts and use hanler to process the message. |
| 187 | */ | 197 | */ |
| 188 | - virtual int parse(ISrsTsHandler* handler); | 198 | + virtual int parse(ISrsTsHandler* ts, ISrsAacHandler* aac); |
| 189 | private: | 199 | private: |
| 190 | /** | 200 | /** |
| 201 | + * parse the ts pieces body. | ||
| 202 | + */ | ||
| 203 | + virtual int parseAac(ISrsAacHandler* handler, char* body, int nb_body, double duration); | ||
| 204 | + virtual int parseTs(ISrsTsHandler* handler, char* body, int nb_body); | ||
| 205 | + /** | ||
| 206 | + * parse the m3u8 specified by url. | ||
| 207 | + */ | ||
| 208 | + virtual int parseM3u8(SrsHttpUri* url, double& td, double& duration); | ||
| 209 | + /** | ||
| 191 | * find the ts piece by its url. | 210 | * find the ts piece by its url. |
| 192 | */ | 211 | */ |
| 193 | virtual SrsTsPiece* find_ts(string url); | 212 | virtual SrsTsPiece* find_ts(string url); |
| @@ -215,17 +234,164 @@ int SrsIngestSrsInput::connect() | @@ -215,17 +234,164 @@ int SrsIngestSrsInput::connect() | ||
| 215 | st_usleep((next_connect_time - now) * 1000); | 234 | st_usleep((next_connect_time - now) * 1000); |
| 216 | } | 235 | } |
| 217 | 236 | ||
| 237 | + // set all ts to dirty. | ||
| 238 | + dirty_all_ts(); | ||
| 239 | + | ||
| 240 | + bool fresh_m3u8 = pieces.empty(); | ||
| 241 | + double td = 0.0; | ||
| 242 | + double duration = 0.0; | ||
| 243 | + if ((ret = parseM3u8(in_hls, td, duration)) != ERROR_SUCCESS) { | ||
| 244 | + return ret; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + // fetch all ts. | ||
| 248 | + fetch_all_ts(fresh_m3u8); | ||
| 249 | + | ||
| 250 | + // remove all dirty ts. | ||
| 251 | + remove_dirty(); | ||
| 252 | + | ||
| 253 | + srs_trace("fetch m3u8 ok, td=%.2f, duration=%.2f, pieces=%d", td, duration, pieces.size()); | ||
| 254 | + | ||
| 255 | + return ret; | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +int SrsIngestSrsInput::parse(ISrsTsHandler* ts, ISrsAacHandler* aac) | ||
| 259 | +{ | ||
| 260 | + int ret = ERROR_SUCCESS; | ||
| 261 | + | ||
| 262 | + for (int i = 0; i < (int)pieces.size(); i++) { | ||
| 263 | + SrsTsPiece* tp = pieces.at(i); | ||
| 264 | + | ||
| 265 | + // sent only once. | ||
| 266 | + if (tp->sent) { | ||
| 267 | + continue; | ||
| 268 | + } | ||
| 269 | + tp->sent = true; | ||
| 270 | + | ||
| 271 | + if (tp->body.empty()) { | ||
| 272 | + continue; | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + srs_trace("proxy the ts to rtmp, ts=%s, duration=%.2f", tp->url.c_str(), tp->duration); | ||
| 276 | + | ||
| 277 | + if (srs_string_ends_with(tp->url, ".ts")) { | ||
| 278 | + if ((ret = parseTs(ts, (char*)tp->body.data(), (int)tp->body.length())) != ERROR_SUCCESS) { | ||
| 279 | + return ret; | ||
| 280 | + } | ||
| 281 | + } else if (srs_string_ends_with(tp->url, ".aac")) { | ||
| 282 | + if ((ret = parseAac(aac, (char*)tp->body.data(), (int)tp->body.length(), tp->duration)) != ERROR_SUCCESS) { | ||
| 283 | + return ret; | ||
| 284 | + } | ||
| 285 | + } else { | ||
| 286 | + srs_warn("ignore unkown piece %s", tp->url.c_str()); | ||
| 287 | + } | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | + return ret; | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +int SrsIngestSrsInput::parseTs(ISrsTsHandler* handler, char* body, int nb_body) | ||
| 294 | +{ | ||
| 295 | + int ret = ERROR_SUCCESS; | ||
| 296 | + | ||
| 297 | + // use stream to parse ts packet. | ||
| 298 | + int nb_packet = (int)nb_body / SRS_TS_PACKET_SIZE; | ||
| 299 | + for (int i = 0; i < nb_packet; i++) { | ||
| 300 | + char* p = (char*)body + (i * SRS_TS_PACKET_SIZE); | ||
| 301 | + if ((ret = stream->initialize(p, SRS_TS_PACKET_SIZE)) != ERROR_SUCCESS) { | ||
| 302 | + return ret; | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + // process each ts packet | ||
| 306 | + if ((ret = context->decode(stream, handler)) != ERROR_SUCCESS) { | ||
| 307 | + // when peer closed, must interrupt parse and reconnect. | ||
| 308 | + if (srs_is_client_gracefully_close(ret)) { | ||
| 309 | + srs_warn("interrupt parse for peer closed. ret=%d", ret); | ||
| 310 | + return ret; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + srs_warn("mpegts: ignore parse ts packet failed. ret=%d", ret); | ||
| 314 | + continue; | ||
| 315 | + } | ||
| 316 | + srs_info("mpegts: parse ts packet completed"); | ||
| 317 | + } | ||
| 318 | + srs_info("mpegts: parse udp packet completed"); | ||
| 319 | + | ||
| 320 | + return ret; | ||
| 321 | +} | ||
| 322 | + | ||
| 323 | +int SrsIngestSrsInput::parseAac(ISrsAacHandler* handler, char* body, int nb_body, double duration) | ||
| 324 | +{ | ||
| 325 | + int ret = ERROR_SUCCESS; | ||
| 326 | + | ||
| 327 | + if ((ret = stream->initialize(body, nb_body)) != ERROR_SUCCESS) { | ||
| 328 | + return ret; | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + // atleast 2bytes. | ||
| 332 | + if (!stream->require(3)) { | ||
| 333 | + ret = ERROR_AAC_BYTES_INVALID; | ||
| 334 | + srs_error("invalid aac, atleast 3bytes. ret=%d", ret); | ||
| 335 | + return ret; | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + u_int8_t id0 = (u_int8_t)body[0]; | ||
| 339 | + u_int8_t id1 = (u_int8_t)body[1]; | ||
| 340 | + u_int8_t id2 = (u_int8_t)body[2]; | ||
| 341 | + | ||
| 342 | + // skip ID3. | ||
| 343 | + if (id0 == 0x49 && id1 == 0x44 && id2 == 0x33) { | ||
| 344 | + /*char id3[] = { | ||
| 345 | + (char)0x49, (char)0x44, (char)0x33, // ID3 | ||
| 346 | + (char)0x03, (char)0x00, // version | ||
| 347 | + (char)0x00, // flags | ||
| 348 | + (char)0x00, (char)0x00, (char)0x00, (char)0x0a, // size | ||
| 349 | + | ||
| 350 | + (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameID | ||
| 351 | + (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameSize | ||
| 352 | + (char)0x00, (char)0x00 // Flags | ||
| 353 | + };*/ | ||
| 354 | + // atleast 10 bytes. | ||
| 355 | + if (!stream->require(10)) { | ||
| 356 | + ret = ERROR_AAC_BYTES_INVALID; | ||
| 357 | + srs_error("invalid aac ID3, atleast 10bytes. ret=%d", ret); | ||
| 358 | + return ret; | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + // ignore ID3 + version + flag. | ||
| 362 | + stream->skip(6); | ||
| 363 | + // read the size of ID3. | ||
| 364 | + u_int32_t nb_id3 = stream->read_4bytes(); | ||
| 365 | + | ||
| 366 | + // read body of ID3 | ||
| 367 | + if (!stream->require(nb_id3)) { | ||
| 368 | + ret = ERROR_AAC_BYTES_INVALID; | ||
| 369 | + srs_error("invalid aac ID3 body, required %dbytes. ret=%d", nb_id3, ret); | ||
| 370 | + return ret; | ||
| 371 | + } | ||
| 372 | + stream->skip(nb_id3); | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | + char* frame = body + stream->pos(); | ||
| 376 | + int frame_size = nb_body - stream->pos(); | ||
| 377 | + return handler->on_aac_frame(frame, frame_size, duration); | ||
| 378 | +} | ||
| 379 | + | ||
| 380 | +int SrsIngestSrsInput::parseM3u8(SrsHttpUri* url, double& td, double& duration) | ||
| 381 | +{ | ||
| 382 | + int ret = ERROR_SUCCESS; | ||
| 383 | + | ||
| 218 | SrsHttpClient client; | 384 | SrsHttpClient client; |
| 219 | - srs_trace("parse input hls %s", in_hls->get_url()); | 385 | + srs_trace("parse input hls %s", url->get_url()); |
| 220 | 386 | ||
| 221 | - if ((ret = client.initialize(in_hls->get_host(), in_hls->get_port())) != ERROR_SUCCESS) { | 387 | + if ((ret = client.initialize(url->get_host(), url->get_port())) != ERROR_SUCCESS) { |
| 222 | srs_error("connect to server failed. ret=%d", ret); | 388 | srs_error("connect to server failed. ret=%d", ret); |
| 223 | return ret; | 389 | return ret; |
| 224 | } | 390 | } |
| 225 | 391 | ||
| 226 | SrsHttpMessage* msg = NULL; | 392 | SrsHttpMessage* msg = NULL; |
| 227 | - if ((ret = client.get(in_hls->get_path(), "", &msg)) != ERROR_SUCCESS) { | ||
| 228 | - srs_error("HTTP GET %s failed. ret=%d", in_hls->get_url(), ret); | 393 | + if ((ret = client.get(url->get_path(), "", &msg)) != ERROR_SUCCESS) { |
| 394 | + srs_error("HTTP GET %s failed. ret=%d", url->get_url(), ret); | ||
| 229 | return ret; | 395 | return ret; |
| 230 | } | 396 | } |
| 231 | 397 | ||
| @@ -243,13 +409,7 @@ int SrsIngestSrsInput::connect() | @@ -243,13 +409,7 @@ int SrsIngestSrsInput::connect() | ||
| 243 | return ret; | 409 | return ret; |
| 244 | } | 410 | } |
| 245 | 411 | ||
| 246 | - // set all ts to dirty. | ||
| 247 | - dirty_all_ts(); | ||
| 248 | - | ||
| 249 | std::string ptl; | 412 | std::string ptl; |
| 250 | - double td = 0.0; | ||
| 251 | - double duration = 0.0; | ||
| 252 | - bool fresh_m3u8 = pieces.empty(); | ||
| 253 | while (!body.empty()) { | 413 | while (!body.empty()) { |
| 254 | size_t pos = string::npos; | 414 | size_t pos = string::npos; |
| 255 | 415 | ||
| @@ -293,6 +453,28 @@ int SrsIngestSrsInput::connect() | @@ -293,6 +453,28 @@ int SrsIngestSrsInput::connect() | ||
| 293 | break; | 453 | break; |
| 294 | } | 454 | } |
| 295 | 455 | ||
| 456 | + // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=73207,CODECS="mp4a.40.2" | ||
| 457 | + if (srs_string_starts_with(line, "#EXT-X-STREAM-INF:")) { | ||
| 458 | + if ((pos = body.find("\n")) == string::npos) { | ||
| 459 | + srs_warn("m3u8 entry unexpected eof, inf=%s", line.c_str()); | ||
| 460 | + break; | ||
| 461 | + } | ||
| 462 | + | ||
| 463 | + std::string m3u8_url = body.substr(0, pos); | ||
| 464 | + body = body.substr(pos + 1); | ||
| 465 | + | ||
| 466 | + if (!srs_string_starts_with(m3u8_url, "http://")) { | ||
| 467 | + m3u8_url = srs_path_dirname(url->get_url()) + "/" + m3u8_url; | ||
| 468 | + } | ||
| 469 | + srs_trace("parse sub m3u8, url=%s", m3u8_url.c_str()); | ||
| 470 | + | ||
| 471 | + if ((ret = url->initialize(m3u8_url)) != ERROR_SUCCESS) { | ||
| 472 | + return ret; | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + return parseM3u8(url, td, duration); | ||
| 476 | + } | ||
| 477 | + | ||
| 296 | // #EXTINF:11.401, | 478 | // #EXTINF:11.401, |
| 297 | // livestream-5.ts | 479 | // livestream-5.ts |
| 298 | // parse each ts entry, expect current line is inf. | 480 | // parse each ts entry, expect current line is inf. |
| @@ -330,60 +512,6 @@ int SrsIngestSrsInput::connect() | @@ -330,60 +512,6 @@ int SrsIngestSrsInput::connect() | ||
| 330 | } | 512 | } |
| 331 | } | 513 | } |
| 332 | 514 | ||
| 333 | - // fetch all ts. | ||
| 334 | - fetch_all_ts(fresh_m3u8); | ||
| 335 | - | ||
| 336 | - // remove all dirty ts. | ||
| 337 | - remove_dirty(); | ||
| 338 | - | ||
| 339 | - srs_trace("fetch m3u8 ok, td=%.2f, duration=%.2f, pieces=%d", td, duration, pieces.size()); | ||
| 340 | - | ||
| 341 | - return ret; | ||
| 342 | -} | ||
| 343 | - | ||
| 344 | -int SrsIngestSrsInput::parse(ISrsTsHandler* handler) | ||
| 345 | -{ | ||
| 346 | - int ret = ERROR_SUCCESS; | ||
| 347 | - | ||
| 348 | - for (int i = 0; i < (int)pieces.size(); i++) { | ||
| 349 | - SrsTsPiece* tp = pieces.at(i); | ||
| 350 | - | ||
| 351 | - // sent only once. | ||
| 352 | - if (tp->sent) { | ||
| 353 | - continue; | ||
| 354 | - } | ||
| 355 | - tp->sent = true; | ||
| 356 | - | ||
| 357 | - if (tp->body.empty()) { | ||
| 358 | - continue; | ||
| 359 | - } | ||
| 360 | - | ||
| 361 | - srs_trace("proxy the ts to rtmp, ts=%s, duration=%.2f", tp->url.c_str(), tp->duration); | ||
| 362 | - | ||
| 363 | - // use stream to parse ts packet. | ||
| 364 | - int nb_packet = (int)tp->body.length() / SRS_TS_PACKET_SIZE; | ||
| 365 | - for (int i = 0; i < nb_packet; i++) { | ||
| 366 | - char* p = (char*)tp->body.data() + (i * SRS_TS_PACKET_SIZE); | ||
| 367 | - if ((ret = stream->initialize(p, SRS_TS_PACKET_SIZE)) != ERROR_SUCCESS) { | ||
| 368 | - return ret; | ||
| 369 | - } | ||
| 370 | - | ||
| 371 | - // process each ts packet | ||
| 372 | - if ((ret = context->decode(stream, handler)) != ERROR_SUCCESS) { | ||
| 373 | - // when peer closed, must interrupt parse and reconnect. | ||
| 374 | - if (srs_is_client_gracefully_close(ret)) { | ||
| 375 | - srs_warn("interrupt parse for peer closed. ret=%d", ret); | ||
| 376 | - return ret; | ||
| 377 | - } | ||
| 378 | - | ||
| 379 | - srs_warn("mpegts: ignore parse ts packet failed. ret=%d", ret); | ||
| 380 | - continue; | ||
| 381 | - } | ||
| 382 | - srs_info("mpegts: parse ts packet completed"); | ||
| 383 | - } | ||
| 384 | - srs_info("mpegts: parse udp packet completed"); | ||
| 385 | - } | ||
| 386 | - | ||
| 387 | return ret; | 515 | return ret; |
| 388 | } | 516 | } |
| 389 | 517 | ||
| @@ -464,17 +592,11 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) | @@ -464,17 +592,11 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) | ||
| 464 | return ret; | 592 | return ret; |
| 465 | } | 593 | } |
| 466 | 594 | ||
| 467 | - size_t pos = string::npos; | ||
| 468 | - | ||
| 469 | SrsHttpClient client; | 595 | SrsHttpClient client; |
| 470 | 596 | ||
| 471 | std::string ts_url = url; | 597 | std::string ts_url = url; |
| 472 | if (!srs_string_starts_with(ts_url, "http://")) { | 598 | if (!srs_string_starts_with(ts_url, "http://")) { |
| 473 | - std::string baseurl = m3u8; | ||
| 474 | - if ((pos = m3u8.rfind("/")) != string::npos) { | ||
| 475 | - baseurl = m3u8.substr(0, pos); | ||
| 476 | - } | ||
| 477 | - ts_url = baseurl + "/" + url; | 599 | + ts_url = srs_path_dirname(m3u8) + "/" + url; |
| 478 | } | 600 | } |
| 479 | 601 | ||
| 480 | SrsHttpUri uri; | 602 | SrsHttpUri uri; |
| @@ -507,13 +629,14 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) | @@ -507,13 +629,14 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) | ||
| 507 | } | 629 | } |
| 508 | 630 | ||
| 509 | // the context to output to rtmp server | 631 | // the context to output to rtmp server |
| 510 | -class SrsIngestSrsOutput : public ISrsTsHandler | 632 | +class SrsIngestSrsOutput : virtual public ISrsTsHandler, virtual public ISrsAacHandler |
| 511 | { | 633 | { |
| 512 | private: | 634 | private: |
| 513 | SrsHttpUri* out_rtmp; | 635 | SrsHttpUri* out_rtmp; |
| 514 | private: | 636 | private: |
| 515 | bool disconnected; | 637 | bool disconnected; |
| 516 | std::multimap<int64_t, SrsTsMessage*> queue; | 638 | std::multimap<int64_t, SrsTsMessage*> queue; |
| 639 | + int64_t raw_aac_dts; | ||
| 517 | private: | 640 | private: |
| 518 | SrsRequest* req; | 641 | SrsRequest* req; |
| 519 | st_netfd_t stfd; | 642 | st_netfd_t stfd; |
| @@ -534,6 +657,7 @@ public: | @@ -534,6 +657,7 @@ public: | ||
| 534 | SrsIngestSrsOutput(SrsHttpUri* rtmp) { | 657 | SrsIngestSrsOutput(SrsHttpUri* rtmp) { |
| 535 | out_rtmp = rtmp; | 658 | out_rtmp = rtmp; |
| 536 | disconnected = false; | 659 | disconnected = false; |
| 660 | + raw_aac_dts = 0; | ||
| 537 | 661 | ||
| 538 | req = NULL; | 662 | req = NULL; |
| 539 | io = NULL; | 663 | io = NULL; |
| @@ -563,7 +687,11 @@ public: | @@ -563,7 +687,11 @@ public: | ||
| 563 | // interface ISrsTsHandler | 687 | // interface ISrsTsHandler |
| 564 | public: | 688 | public: |
| 565 | virtual int on_ts_message(SrsTsMessage* msg); | 689 | virtual int on_ts_message(SrsTsMessage* msg); |
| 690 | +// interface IAacHandler | ||
| 691 | +public: | ||
| 692 | + virtual int on_aac_frame(char* frame, int frame_size, double duration); | ||
| 566 | private: | 693 | private: |
| 694 | + virtual int do_on_aac_frame(SrsStream* avs, double duration); | ||
| 567 | virtual int parse_message_queue(); | 695 | virtual int parse_message_queue(); |
| 568 | virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs); | 696 | virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs); |
| 569 | virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); | 697 | virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); |
| @@ -661,6 +789,76 @@ int SrsIngestSrsOutput::on_ts_message(SrsTsMessage* msg) | @@ -661,6 +789,76 @@ int SrsIngestSrsOutput::on_ts_message(SrsTsMessage* msg) | ||
| 661 | return ret; | 789 | return ret; |
| 662 | } | 790 | } |
| 663 | 791 | ||
| 792 | +int SrsIngestSrsOutput::on_aac_frame(char* frame, int frame_size, double duration) | ||
| 793 | +{ | ||
| 794 | + int ret = ERROR_SUCCESS; | ||
| 795 | + | ||
| 796 | + srs_trace("handle aac frames, size=%dB, duration=%.2f, dts=%"PRId64, frame_size, duration, raw_aac_dts); | ||
| 797 | + | ||
| 798 | + SrsStream stream; | ||
| 799 | + if ((ret = stream.initialize(frame, frame_size)) != ERROR_SUCCESS) { | ||
| 800 | + return ret; | ||
| 801 | + } | ||
| 802 | + | ||
| 803 | + return do_on_aac_frame(&stream, duration); | ||
| 804 | +} | ||
| 805 | + | ||
| 806 | +int SrsIngestSrsOutput::do_on_aac_frame(SrsStream* avs, double duration) | ||
| 807 | +{ | ||
| 808 | + int ret = ERROR_SUCCESS; | ||
| 809 | + | ||
| 810 | + // ts tbn to flv tbn. | ||
| 811 | + u_int32_t dts = (u_int32_t)raw_aac_dts; | ||
| 812 | + raw_aac_dts += (int64_t)(duration * 1000); | ||
| 813 | + | ||
| 814 | + // got the next msg to calc the delta duration for each audio. | ||
| 815 | + u_int32_t max_dts = dts + (u_int32_t)(duration * 1000); | ||
| 816 | + | ||
| 817 | + // send each frame. | ||
| 818 | + while (!avs->empty()) { | ||
| 819 | + char* frame = NULL; | ||
| 820 | + int frame_size = 0; | ||
| 821 | + SrsRawAacStreamCodec codec; | ||
| 822 | + if ((ret = aac->adts_demux(avs, &frame, &frame_size, codec)) != ERROR_SUCCESS) { | ||
| 823 | + return ret; | ||
| 824 | + } | ||
| 825 | + | ||
| 826 | + // ignore invalid frame, | ||
| 827 | + // * atleast 1bytes for aac to decode the data. | ||
| 828 | + if (frame_size <= 0) { | ||
| 829 | + continue; | ||
| 830 | + } | ||
| 831 | + srs_info("mpegts: demux aac frame size=%d, dts=%d", frame_size, dts); | ||
| 832 | + | ||
| 833 | + // generate sh. | ||
| 834 | + if (aac_specific_config.empty()) { | ||
| 835 | + std::string sh; | ||
| 836 | + if ((ret = aac->mux_sequence_header(&codec, sh)) != ERROR_SUCCESS) { | ||
| 837 | + return ret; | ||
| 838 | + } | ||
| 839 | + aac_specific_config = sh; | ||
| 840 | + | ||
| 841 | + codec.aac_packet_type = 0; | ||
| 842 | + | ||
| 843 | + if ((ret = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, dts)) != ERROR_SUCCESS) { | ||
| 844 | + return ret; | ||
| 845 | + } | ||
| 846 | + } | ||
| 847 | + | ||
| 848 | + // audio raw data. | ||
| 849 | + codec.aac_packet_type = 1; | ||
| 850 | + if ((ret = write_audio_raw_frame(frame, frame_size, &codec, dts)) != ERROR_SUCCESS) { | ||
| 851 | + return ret; | ||
| 852 | + } | ||
| 853 | + | ||
| 854 | + // calc the delta of dts, when previous frame output. | ||
| 855 | + u_int32_t delta = (duration * 1000) / (avs->size() / frame_size); | ||
| 856 | + dts = (u_int32_t)(srs_min(max_dts, dts + delta)); | ||
| 857 | + } | ||
| 858 | + | ||
| 859 | + return ret; | ||
| 860 | +} | ||
| 861 | + | ||
| 664 | int SrsIngestSrsOutput::parse_message_queue() | 862 | int SrsIngestSrsOutput::parse_message_queue() |
| 665 | { | 863 | { |
| 666 | int ret = ERROR_SUCCESS; | 864 | int ret = ERROR_SUCCESS; |
| @@ -914,7 +1112,7 @@ int SrsIngestSrsOutput::on_ts_audio(SrsTsMessage* msg, SrsStream* avs) | @@ -914,7 +1112,7 @@ int SrsIngestSrsOutput::on_ts_audio(SrsTsMessage* msg, SrsStream* avs) | ||
| 914 | // ts tbn to flv tbn. | 1112 | // ts tbn to flv tbn. |
| 915 | u_int32_t dts = (u_int32_t)(msg->dts / 90); | 1113 | u_int32_t dts = (u_int32_t)(msg->dts / 90); |
| 916 | 1114 | ||
| 917 | - // got the next video to calc the delta duration for each audio. | 1115 | + // got the next msg to calc the delta duration for each audio. |
| 918 | u_int32_t duration = 0; | 1116 | u_int32_t duration = 0; |
| 919 | if (!queue.empty()) { | 1117 | if (!queue.empty()) { |
| 920 | SrsTsMessage* nm = queue.begin()->second; | 1118 | SrsTsMessage* nm = queue.begin()->second; |
| @@ -992,6 +1190,8 @@ int SrsIngestSrsOutput::rtmp_write_packet(char type, u_int32_t timestamp, char* | @@ -992,6 +1190,8 @@ int SrsIngestSrsOutput::rtmp_write_packet(char type, u_int32_t timestamp, char* | ||
| 992 | } | 1190 | } |
| 993 | srs_assert(msg); | 1191 | srs_assert(msg); |
| 994 | 1192 | ||
| 1193 | + srs_info("RTMP type=%d, dts=%d, size=%d", type, timestamp, size); | ||
| 1194 | + | ||
| 995 | // send out encoded msg. | 1195 | // send out encoded msg. |
| 996 | if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { | 1196 | if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { |
| 997 | return ret; | 1197 | return ret; |
| @@ -1016,13 +1216,12 @@ int SrsIngestSrsOutput::connect() | @@ -1016,13 +1216,12 @@ int SrsIngestSrsOutput::connect() | ||
| 1016 | if (!req) { | 1216 | if (!req) { |
| 1017 | req = new SrsRequest(); | 1217 | req = new SrsRequest(); |
| 1018 | 1218 | ||
| 1019 | - size_t pos = string::npos; | ||
| 1020 | string uri = req->tcUrl = out_rtmp->get_url(); | 1219 | string uri = req->tcUrl = out_rtmp->get_url(); |
| 1021 | 1220 | ||
| 1022 | // tcUrl, stream | 1221 | // tcUrl, stream |
| 1023 | - if ((pos = uri.rfind("/")) != string::npos) { | ||
| 1024 | - req->stream = uri.substr(pos + 1); | ||
| 1025 | - req->tcUrl = uri = uri.substr(0, pos); | 1222 | + if (srs_string_contains(uri, "/")) { |
| 1223 | + req->stream = srs_path_basename(uri); | ||
| 1224 | + req->tcUrl = uri = srs_path_dirname(uri); | ||
| 1026 | } | 1225 | } |
| 1027 | 1226 | ||
| 1028 | srs_discovery_tc_url(req->tcUrl, | 1227 | srs_discovery_tc_url(req->tcUrl, |
| @@ -1155,7 +1354,7 @@ public: | @@ -1155,7 +1354,7 @@ public: | ||
| 1155 | return ret; | 1354 | return ret; |
| 1156 | } | 1355 | } |
| 1157 | 1356 | ||
| 1158 | - if ((ret = ic->parse(oc)) != ERROR_SUCCESS) { | 1357 | + if ((ret = ic->parse(oc, oc)) != ERROR_SUCCESS) { |
| 1159 | srs_warn("proxy ts to rtmp failed. ret=%d", ret); | 1358 | srs_warn("proxy ts to rtmp failed. ret=%d", ret); |
| 1160 | return ret; | 1359 | return ret; |
| 1161 | } | 1360 | } |
| @@ -362,9 +362,12 @@ int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame | @@ -362,9 +362,12 @@ int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame | ||
| 362 | * and set to ‘0’ if the audio data are MPEG-4. See also ISO/IEC 11172-3, subclause 2.4.2.3. | 362 | * and set to ‘0’ if the audio data are MPEG-4. See also ISO/IEC 11172-3, subclause 2.4.2.3. |
| 363 | */ | 363 | */ |
| 364 | if (id != 0x01) { | 364 | if (id != 0x01) { |
| 365 | - ret = ERROR_ADTS_ID_NOT_AAC; | ||
| 366 | - srs_warn("adts: id must be 1(aac), actual 0(mp4a). ret=%d", ret); | ||
| 367 | - return ret; | 365 | + srs_info("adts: id must be 1(aac), actual 0(mp4a). ret=%d", ret); |
| 366 | + | ||
| 367 | + // well, some system always use 0, but actually is aac format. | ||
| 368 | + // for example, houjian vod ts always set the aac id to 0, actually 1. | ||
| 369 | + // we just ignore it, and alwyas use 1(aac) to demux. | ||
| 370 | + id = 0x01; | ||
| 368 | } | 371 | } |
| 369 | 372 | ||
| 370 | int16_t sfiv = stream->read_2bytes(); | 373 | int16_t sfiv = stream->read_2bytes(); |
-
请 注册 或 登录 后发表评论