1.add -d to indicate output as individual video segment if the timeline is not continuous
2.support fill blank video with last video frame
正在显示
2 个修改的文件
包含
191 行增加
和
33 行删除
| @@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
| 12 | 12 | ||
| 13 | bool only_print = false; | 13 | bool only_print = false; |
| 14 | bool keep_tmp_files = false; | 14 | bool keep_tmp_files = false; |
| 15 | +bool out_one_video = true; | ||
| 15 | using namespace std; | 16 | using namespace std; |
| 16 | 17 | ||
| 17 | enum media_type{ | 18 | enum media_type{ |
| @@ -57,6 +58,7 @@ vector<fileinfo> media_files; | @@ -57,6 +58,7 @@ vector<fileinfo> media_files; | ||
| 57 | const char * MERGED_PREFIX = ""; | 58 | const char * MERGED_PREFIX = ""; |
| 58 | const char * PIP_PREFIX = "pip_"; | 59 | const char * PIP_PREFIX = "pip_"; |
| 59 | 60 | ||
| 61 | +float get_file_duration(const char *mediafile, bool bVideo); | ||
| 60 | 62 | ||
| 61 | void run_shell_cmd(const char * cmd) | 63 | void run_shell_cmd(const char * cmd) |
| 62 | { | 64 | { |
| @@ -241,6 +243,7 @@ void get_video_first_frame_jpeg(const char * video, const char * destfile) | @@ -241,6 +243,7 @@ void get_video_first_frame_jpeg(const char * video, const char * destfile) | ||
| 241 | run_shell_cmd(buf); | 243 | run_shell_cmd(buf); |
| 242 | } | 244 | } |
| 243 | 245 | ||
| 246 | + | ||
| 244 | void merge_audio_pic(const char * audio, const char * picfile, const char * destfile) | 247 | void merge_audio_pic(const char * audio, const char * picfile, const char * destfile) |
| 245 | { | 248 | { |
| 246 | char buf[2048]; | 249 | char buf[2048]; |
| @@ -274,7 +277,7 @@ void merge_audio_video(const char * audio, const char * video, int rotate, const | @@ -274,7 +277,7 @@ void merge_audio_video(const char * audio, const char * video, int rotate, const | ||
| 274 | void merge_video_pip(const char * ch0, const char * ch1, const char * destfile) | 277 | void merge_video_pip(const char * ch0, const char * ch1, const char * destfile) |
| 275 | { | 278 | { |
| 276 | char buf[2048]; | 279 | char buf[2048]; |
| 277 | - sprintf(buf, "ffmpeg -i %s -i %s %s %s %s", ch0, ch1, pip_param, av_codec_param, destfile); | 280 | + sprintf(buf, "ffmpeg -y -i %s -i %s %s %s %s", ch0, ch1, pip_param, av_codec_param, destfile); |
| 278 | run_shell_cmd(buf); | 281 | run_shell_cmd(buf); |
| 279 | } | 282 | } |
| 280 | 283 | ||
| @@ -382,7 +385,6 @@ float parse_ffmpeg_duration(const char * file) | @@ -382,7 +385,6 @@ float parse_ffmpeg_duration(const char * file) | ||
| 382 | 385 | ||
| 383 | if (hms.size() != 3) { | 386 | if (hms.size() != 3) { |
| 384 | printf("\nerro parsing duration in %s, the duration string is:%s\n", file, content); | 387 | printf("\nerro parsing duration in %s, the duration string is:%s\n", file, content); |
| 385 | - delete content; | ||
| 386 | return -1.0; | 388 | return -1.0; |
| 387 | } | 389 | } |
| 388 | 390 | ||
| @@ -568,7 +570,92 @@ char silence_aac_file[1024]; | @@ -568,7 +570,92 @@ char silence_aac_file[1024]; | ||
| 568 | char out_info_file[1024]; | 570 | char out_info_file[1024]; |
| 569 | FILE * fp_out_info = NULL; | 571 | FILE * fp_out_info = NULL; |
| 570 | 572 | ||
| 573 | +void merge_pic_silence(const char * pic, float duration, const char * destfile) | ||
| 574 | +{ | ||
| 575 | + char buf[2048]; | ||
| 576 | + sprintf(buf, "ffmpeg -y -loop 1 -i %s -i %s -loop 0 -t %.3f -shortest %s %s %s", pic, silence_aac_file, duration, acodec_param, vcodec_param, destfile); | ||
| 577 | + run_shell_cmd(buf); | ||
| 578 | +} | ||
| 579 | + | ||
| 580 | +int parse_ffmpeg_lastframe(const char * file) | ||
| 581 | +{ | ||
| 582 | + FILE * fp = fopen(file, "rb"); | ||
| 583 | + if (!fp) { | ||
| 584 | + printf("\nOpen %s error\n", file); | ||
| 585 | + return -1.0; | ||
| 586 | + } | ||
| 587 | + fseek(fp, 0l, SEEK_END); | ||
| 588 | + long file_len = ftell(fp); | ||
| 589 | + fseek(fp, 0l, SEEK_SET); | ||
| 590 | + | ||
| 591 | + char * content = new char[file_len + 1]; | ||
| 592 | + fread(content, 1, file_len, fp); | ||
| 593 | + fclose(fp); | ||
| 594 | + | ||
| 595 | + content[file_len] = 0; | ||
| 596 | + | ||
| 597 | + if (!keep_tmp_files) { | ||
| 598 | + remove_file(file); | ||
| 599 | + } | ||
| 600 | + | ||
| 601 | + char * pLastFrame = strstr(content, "frame="); | ||
| 602 | + if (!pLastFrame){ | ||
| 603 | + printf("\ncan't find 'frame' in %s\n", file); | ||
| 604 | + delete content; | ||
| 605 | + return 0; | ||
| 606 | + } | ||
| 607 | + while (1){ | ||
| 608 | + char * pFrame = strstr(pLastFrame + 6, "frame="); | ||
| 609 | + if (!pFrame){ | ||
| 610 | + break; | ||
| 611 | + } | ||
| 612 | + pLastFrame = pFrame; | ||
| 613 | + } | ||
| 614 | + | ||
| 615 | + int frame = atoi(pLastFrame + 6); | ||
| 571 | 616 | ||
| 617 | + delete content; | ||
| 618 | + return frame; | ||
| 619 | +} | ||
| 620 | + | ||
| 621 | +void get_video_last_frame_jpeg(const char * video, const char * destfile) { | ||
| 622 | + char buf[2048]; | ||
| 623 | + //first ,get | ||
| 624 | + float duration = get_file_duration(video, true); | ||
| 625 | + if (duration > 0) { | ||
| 626 | + float start = 0; | ||
| 627 | + if (duration > 1) { | ||
| 628 | + start = duration - 1; | ||
| 629 | + } | ||
| 630 | +#ifdef WIN32 | ||
| 631 | + sprintf(buf, "ffmpeg -y -i %s -ss %.1f -an %s_last_%%d.jpg > %s.txt 2>&1", video, start, video,video); | ||
| 632 | +#else | ||
| 633 | + sprintf(buf, "ffmpeg -y -i %s -ss %.1f -an %s_last_%%d.jpg &> %s.txt", video, start, video, video); | ||
| 634 | +#endif | ||
| 635 | + run_shell_cmd(buf); | ||
| 636 | + sprintf(buf,"%s.txt", video); | ||
| 637 | + int last_frame = parse_ffmpeg_lastframe(buf); | ||
| 638 | + if (last_frame > 0) { | ||
| 639 | +#ifdef WIN32 | ||
| 640 | + sprintf(buf, "copy %s_last_%d.jpg %s", video,last_frame,destfile); | ||
| 641 | +#else | ||
| 642 | + sprintf(buf, "cp %s_last_%d.jpg %s", video, last_frame,destfile); | ||
| 643 | +#endif | ||
| 644 | + run_shell_cmd(buf); | ||
| 645 | + for (int i = 1; i <= last_frame; i++) { | ||
| 646 | + sprintf(buf, "%s_last_%d.jpg", video, i); | ||
| 647 | + tmp_files.push_back(buf); | ||
| 648 | + } | ||
| 649 | + } | ||
| 650 | + } | ||
| 651 | + else { | ||
| 652 | +#ifdef WIN32 | ||
| 653 | + sprintf(buf, "copy %s %s", blank_pic_file, destfile); | ||
| 654 | +#else | ||
| 655 | + sprintf(buf, "cp %s %s", blank_pic_file, destfile); | ||
| 656 | +#endif | ||
| 657 | + } | ||
| 658 | +} | ||
| 572 | 659 | ||
| 573 | void add_media_info(media_info m) | 660 | void add_media_info(media_info m) |
| 574 | { | 661 | { |
| @@ -1190,12 +1277,25 @@ int get_output_file_name(int i, const char * filename, const char * prefix,char | @@ -1190,12 +1277,25 @@ int get_output_file_name(int i, const char * filename, const char * prefix,char | ||
| 1190 | return 0; | 1277 | return 0; |
| 1191 | } | 1278 | } |
| 1192 | 1279 | ||
| 1193 | -bool is_need_output(int nOutPutFile, vector<media_info> & cur_processing, const char * first_file, char * outputfile, const char * prefix="") | 1280 | +bool is_need_output(int & nOutPutFile, vector<media_info> & cur_processing, const char * first_file, char * outputfile, const char * prefix="") |
| 1194 | { | 1281 | { |
| 1195 | if (sorted_infos.size()) { | 1282 | if (sorted_infos.size()) { |
| 1196 | - int lastEnd = cur_processing[cur_processing.size() - 1].type_time; | ||
| 1197 | - int nextStart = sorted_infos.front().type_time; | ||
| 1198 | - if (nextStart - lastEnd < 0.2) { | 1283 | + float lastEnd = cur_processing[cur_processing.size() - 1].type_time; |
| 1284 | + float nextStart = sorted_infos.front().type_time; | ||
| 1285 | + float gap = nextStart - lastEnd; | ||
| 1286 | + if ( gap < 0.2) { | ||
| 1287 | + return false; | ||
| 1288 | + } | ||
| 1289 | + else if(out_one_video){ | ||
| 1290 | + string last_merged_video = merged_files[merged_files.size() - 1]; | ||
| 1291 | + char buf[1024]; | ||
| 1292 | + sprintf(buf, "%s_last_frame.jpg", last_merged_video.c_str()); | ||
| 1293 | + get_video_last_frame_jpeg(last_merged_video.c_str(), buf); | ||
| 1294 | + char buf_dest[1024]; | ||
| 1295 | + sprintf(buf_dest, "%d.ts", nOutPutFile++); | ||
| 1296 | + merge_pic_silence(buf, gap, buf_dest); | ||
| 1297 | + merged_files.push_back(buf_dest); | ||
| 1298 | + tmp_files.push_back(buf); | ||
| 1199 | return false; | 1299 | return false; |
| 1200 | } | 1300 | } |
| 1201 | } | 1301 | } |
| @@ -1380,7 +1480,7 @@ int process_merged_files_to_pip_files() | @@ -1380,7 +1480,7 @@ int process_merged_files_to_pip_files() | ||
| 1380 | return 0; | 1480 | return 0; |
| 1381 | } | 1481 | } |
| 1382 | 1482 | ||
| 1383 | -int readfile(char * filename) | 1483 | +int readfile(const char * filename) |
| 1384 | { | 1484 | { |
| 1385 | init_read_file(); | 1485 | init_read_file(); |
| 1386 | media_files.clear(); | 1486 | media_files.clear(); |
| @@ -1454,7 +1554,7 @@ float get_start_time_from_filename(const char * filename) | @@ -1454,7 +1554,7 @@ float get_start_time_from_filename(const char * filename) | ||
| 1454 | 1554 | ||
| 1455 | vector<string> all_input_files_for_pip; | 1555 | vector<string> all_input_files_for_pip; |
| 1456 | 1556 | ||
| 1457 | -int readfile(char * filename, int channel) | 1557 | +int readfile(const char * filename, int channel) |
| 1458 | { | 1558 | { |
| 1459 | init_read_file(); | 1559 | init_read_file(); |
| 1460 | 1560 | ||
| @@ -1535,35 +1635,79 @@ void load_codec_param() | @@ -1535,35 +1635,79 @@ void load_codec_param() | ||
| 1535 | } | 1635 | } |
| 1536 | } | 1636 | } |
| 1537 | 1637 | ||
| 1538 | -void get_outinfo_file_name(char * input1 , char * input2) | 1638 | +void get_outinfo_file_name(char * input) |
| 1539 | { | 1639 | { |
| 1540 | - strcpy(out_info_file, input1); | 1640 | + strcpy(out_info_file, input); |
| 1541 | char * p = strstr(out_info_file, "."); | 1641 | char * p = strstr(out_info_file, "."); |
| 1542 | if (p) { | 1642 | if (p) { |
| 1543 | *p = 0; | 1643 | *p = 0; |
| 1544 | } | 1644 | } |
| 1545 | - strcat(out_info_file, "__"); | ||
| 1546 | - strcat(out_info_file, input2); | 1645 | + strcat(out_info_file, "_out.txt"); |
| 1547 | } | 1646 | } |
| 1548 | 1647 | ||
| 1549 | -void get_outinfo_file_name(char * input) | 1648 | +int process_record_info_to_ts(char * record_info, vector<string> & merged_info1, vector<string> & merged_info2) |
| 1550 | { | 1649 | { |
| 1551 | - strcpy(out_info_file, input); | ||
| 1552 | - char * p = strstr(out_info_file, "."); | ||
| 1553 | - if (p) { | ||
| 1554 | - *p = 0; | 1650 | + ifstream fin(record_info); |
| 1651 | + if (!fin) { | ||
| 1652 | + return -1; | ||
| 1555 | } | 1653 | } |
| 1556 | - strcat(out_info_file, "_out.txt"); | 1654 | + |
| 1655 | + const int LINE_LENGTH = 1000; | ||
| 1656 | + char str[LINE_LENGTH]; | ||
| 1657 | + bool bInTeacher = false; | ||
| 1658 | + bool bInStudent = false; | ||
| 1659 | + int nstudent = 0; | ||
| 1660 | + while (fin.getline(str, LINE_LENGTH)) | ||
| 1661 | + { | ||
| 1662 | + if (!strncmp(str, "teacher:", 8)){ | ||
| 1663 | + bInTeacher = true; | ||
| 1664 | + bInStudent = false; | ||
| 1665 | + continue; | ||
| 1666 | + } | ||
| 1667 | + else if (!strncmp(str, "student:", 8)){ | ||
| 1668 | + bInTeacher = false; | ||
| 1669 | + bInStudent = true; | ||
| 1670 | + continue; | ||
| 1671 | + } | ||
| 1672 | + else if (strlen(str) < 20){ | ||
| 1673 | + continue;//assume the file name > 20 | ||
| 1674 | + } | ||
| 1675 | + else if (bInTeacher){ | ||
| 1676 | + readfile(str); | ||
| 1677 | + get_outinfo_file_name(str); | ||
| 1678 | + merged_info1.push_back(out_info_file); | ||
| 1679 | + process_record_file_to_ts(); | ||
| 1680 | + } | ||
| 1681 | + else if (bInStudent){ | ||
| 1682 | + readfile(str); | ||
| 1683 | + get_outinfo_file_name(str); | ||
| 1684 | + merged_info2.push_back(out_info_file); | ||
| 1685 | + process_record_file_to_ts(); | ||
| 1686 | + } | ||
| 1687 | + } | ||
| 1688 | + return 0; | ||
| 1557 | } | 1689 | } |
| 1558 | 1690 | ||
| 1691 | +//#define TEST | ||
| 1559 | int main(int argc, char * argv[]) | 1692 | int main(int argc, char * argv[]) |
| 1560 | { | 1693 | { |
| 1561 | - if (argc < 3) { | ||
| 1562 | - printf(" merge_pip 1.0.1\n"); | 1694 | +#ifdef TEST |
| 1695 | + const char * video = "D:\\media\\talk\\20181112\\talk915_824250397_105638\\137786519_20181112105817677.ts"; | ||
| 1696 | + const char * dest = "D:\\media\\talk\\20181112\\talk915_824250397_105638\\137786519_20181112105817677.ts.jpg"; | ||
| 1697 | + get_config_path(); | ||
| 1698 | + load_codec_param(); | ||
| 1699 | + init_merge_av(); | ||
| 1700 | + get_video_last_frame_jpeg(video, dest); | ||
| 1701 | + merge_pic_silence(dest, 1.56, "D:\\media\\talk\\20181112\\talk915_824250397_105638\\137786519_20181112105817677_last_s156.ts"); | ||
| 1702 | +#endif | ||
| 1703 | + | ||
| 1704 | + if (argc < 2) { | ||
| 1705 | + printf(" merge_pip 1.0.2\n"); | ||
| 1563 | printf(" run ffmpeg to merge video files to one pip video according to record info file,\nusage:"); | 1706 | printf(" run ffmpeg to merge video files to one pip video according to record info file,\nusage:"); |
| 1564 | - printf("\n %s record_info_filename1 record_filename2 [-p] [-k]", argv[0]); | 1707 | + printf("\n %s record_info_filename [-p] [-k]", argv[0]); |
| 1565 | printf("\n -p :only print the command,don't run ffmpeg"); | 1708 | printf("\n -p :only print the command,don't run ffmpeg"); |
| 1566 | printf("\n -k :keep the temp files\n"); | 1709 | printf("\n -k :keep the temp files\n"); |
| 1710 | + printf("\n -d :individual files for different time segment\n"); | ||
| 1567 | return -1; | 1711 | return -1; |
| 1568 | } | 1712 | } |
| 1569 | 1713 | ||
| @@ -1573,7 +1717,7 @@ int main(int argc, char * argv[]) | @@ -1573,7 +1717,7 @@ int main(int argc, char * argv[]) | ||
| 1573 | 1717 | ||
| 1574 | bool bmerge_files = true; | 1718 | bool bmerge_files = true; |
| 1575 | 1719 | ||
| 1576 | - for (int i = 3; i < argc; i++){ | 1720 | + for (int i = 2; i < argc; i++){ |
| 1577 | if (!strcmp(argv[i], "-p")){ | 1721 | if (!strcmp(argv[i], "-p")){ |
| 1578 | only_print = true; | 1722 | only_print = true; |
| 1579 | } | 1723 | } |
| @@ -1583,11 +1727,14 @@ int main(int argc, char * argv[]) | @@ -1583,11 +1727,14 @@ int main(int argc, char * argv[]) | ||
| 1583 | else if (!strcmp(argv[i], "-n")){ | 1727 | else if (!strcmp(argv[i], "-n")){ |
| 1584 | bmerge_files = false; | 1728 | bmerge_files = false; |
| 1585 | } | 1729 | } |
| 1730 | + else if (!strcmp(argv[i], "-d")){ | ||
| 1731 | + out_one_video = false; | ||
| 1732 | + } | ||
| 1586 | } | 1733 | } |
| 1587 | 1734 | ||
| 1588 | - char merged_info1[1024]; | ||
| 1589 | - char merged_info2[1024]; | ||
| 1590 | - | 1735 | + vector<string> merged_info1; |
| 1736 | + vector<string> merged_info2; | ||
| 1737 | +#if 0 | ||
| 1591 | if (bmerge_files) { | 1738 | if (bmerge_files) { |
| 1592 | if (readfile(argv[1]) < 0) { | 1739 | if (readfile(argv[1]) < 0) { |
| 1593 | printf("open file: %s error", argv[1]); | 1740 | printf("open file: %s error", argv[1]); |
| @@ -1613,27 +1760,38 @@ int main(int argc, char * argv[]) | @@ -1613,27 +1760,38 @@ int main(int argc, char * argv[]) | ||
| 1613 | strcpy(merged_info1, argv[1]); | 1760 | strcpy(merged_info1, argv[1]); |
| 1614 | strcpy(merged_info2, argv[2]); | 1761 | strcpy(merged_info2, argv[2]); |
| 1615 | } | 1762 | } |
| 1763 | +#else | ||
| 1764 | + process_record_info_to_ts(argv[1], merged_info1, merged_info2); | ||
| 1616 | 1765 | ||
| 1766 | +#endif | ||
| 1617 | 1767 | ||
| 1618 | media_files.clear(); | 1768 | media_files.clear(); |
| 1619 | - if (readfile(merged_info1, 0) < 0) { | ||
| 1620 | - printf("open file: %s error", merged_info1); | ||
| 1621 | - return -2; | 1769 | + for (int i = 0; i < merged_info1.size(); i++) { |
| 1770 | + if (readfile(merged_info1[i].c_str(), 0) < 0) { | ||
| 1771 | + printf("open file: %s error", merged_info1[i].c_str()); | ||
| 1772 | + return -2; | ||
| 1773 | + } | ||
| 1622 | } | 1774 | } |
| 1623 | 1775 | ||
| 1624 | - if (readfile(merged_info2, 1) < 0) { | ||
| 1625 | - printf("open file: %s error", merged_info2); | ||
| 1626 | - return -2; | 1776 | + for (int i = 0; i < merged_info2.size(); i++) { |
| 1777 | + if (readfile(merged_info2[i].c_str(), 1) < 0) { | ||
| 1778 | + printf("open file: %s error", merged_info2[i].c_str()); | ||
| 1779 | + return -2; | ||
| 1780 | + } | ||
| 1627 | } | 1781 | } |
| 1628 | 1782 | ||
| 1783 | +#if 0 | ||
| 1629 | get_outinfo_file_name(argv[1], argv[2]); | 1784 | get_outinfo_file_name(argv[1], argv[2]); |
| 1785 | +#else | ||
| 1786 | + get_outinfo_file_name(argv[1]); | ||
| 1787 | +#endif | ||
| 1630 | 1788 | ||
| 1631 | process_merged_files_to_pip_files(); | 1789 | process_merged_files_to_pip_files(); |
| 1632 | 1790 | ||
| 1633 | if (!keep_tmp_files && bmerge_files) { | 1791 | if (!keep_tmp_files && bmerge_files) { |
| 1634 | removefiles(all_input_files_for_pip); | 1792 | removefiles(all_input_files_for_pip); |
| 1635 | - remove_file(merged_info1); | ||
| 1636 | - remove_file(merged_info2); | 1793 | + removefiles(merged_info1); |
| 1794 | + removefiles(merged_info2); | ||
| 1637 | } | 1795 | } |
| 1638 | 1796 | ||
| 1639 | return 0; | 1797 | return 0; |
-
请 注册 或 登录 后发表评论