generate_h264.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env python3
  2. import os
  3. import getopt
  4. import sys
  5. import glob
  6. from functools import reduce
  7. from typing import Optional, List
  8. class H264ByteStream:
  9. @staticmethod
  10. def nalu_type(nalu: bytes) -> int:
  11. return nalu[0] & 0x1F
  12. @staticmethod
  13. def merge_sample(sample: List[bytes]) -> bytes:
  14. result = bytes()
  15. for nalu in sample:
  16. result += len(nalu).to_bytes(4, byteorder='big') + nalu
  17. return result
  18. @staticmethod
  19. def reduce_nalus_to_samples(samples: List[List[bytes]], current: bytes) -> List[List[bytes]]:
  20. last_nalus = samples[-1]
  21. samples[-1] = last_nalus + [current]
  22. if H264ByteStream.nalu_type(current) in [1, 5]:
  23. samples.append([])
  24. return samples
  25. def __init__(self, file_name: str):
  26. with open(file_name, "rb") as file:
  27. byte_stream = file.read()
  28. long_split = byte_stream.split(b"\x00\x00\x00\x01")
  29. splits = reduce(lambda acc, x: acc + x.split(b"\x00\x00\x01"), long_split, [])
  30. nalus = filter(lambda x: len(x) > 0, splits)
  31. self.samples = list(
  32. filter(lambda x: len(x) > 0, reduce(H264ByteStream.reduce_nalus_to_samples, nalus, [[]])))
  33. def generate(input_file: str, output_dir: str, max_samples: Optional[int], fps: Optional[int]):
  34. if output_dir[-1] != "/":
  35. output_dir += "/"
  36. if os.path.isdir(output_dir):
  37. files_to_delete = glob.glob(output_dir + "*.h264")
  38. if len(files_to_delete) > 0:
  39. print("Remove following files?")
  40. for file in files_to_delete:
  41. print(file)
  42. response = input("Remove files? [y/n] ").lower()
  43. if response != "y" and response != "yes":
  44. print("Cancelling...")
  45. return
  46. print("Removing files")
  47. for file in files_to_delete:
  48. os.remove(file)
  49. else:
  50. os.makedirs(output_dir, exist_ok=True)
  51. video_stream_file = "_video_stream.h264"
  52. if os.path.isfile(video_stream_file):
  53. os.remove(video_stream_file)
  54. fps_line = "" if fps is None else "-filter:v fps=fps={} ".format(fps)
  55. command = 'ffmpeg -i {} -an -vcodec libx264 -preset slow -profile baseline {}{}'.format(input_file, fps_line,
  56. video_stream_file)
  57. os.system(command)
  58. data = H264ByteStream(video_stream_file)
  59. index = 0
  60. for sample in data.samples[:max_samples]:
  61. name = "{}sample-{}.h264".format(output_dir, index)
  62. index += 1
  63. with open(name, 'wb') as file:
  64. merged_sample = H264ByteStream.merge_sample(sample)
  65. file.write(merged_sample)
  66. os.remove(video_stream_file)
  67. def main(argv):
  68. input_file = None
  69. default_output_dir = "h264/"
  70. output_dir = default_output_dir
  71. max_samples = None
  72. fps = None
  73. try:
  74. opts, args = getopt.getopt(argv, "hi:o:m:f:", ["help", "ifile=", "odir=", "max=", "fps"])
  75. except getopt.GetoptError:
  76. print('generate_h264.py -i <input_files> [-o <output_files>] [-m <max_samples>] [-f <fps>] [-h]')
  77. sys.exit(2)
  78. for opt, arg in opts:
  79. if opt in ("-h", "--help"):
  80. print("Usage: generate_h264.py -i <input_files> [-o <output_files>] [-m <max_samples>] [-f <fps>] [-h]")
  81. print("Arguments:")
  82. print("\t-i,--ifile: Input file")
  83. print("\t-o,--odir: Output directory (default: " + default_output_dir + ")")
  84. print("\t-m,--max: Maximum generated samples")
  85. print("\t-f,--fps: Output fps")
  86. print("\t-h,--help: Print this help and exit")
  87. sys.exit()
  88. elif opt in ("-i", "--ifile"):
  89. input_file = arg
  90. elif opt in ("-o", "--ofile"):
  91. output_dir = arg
  92. elif opt in ("-m", "--max"):
  93. max_samples = int(arg)
  94. elif opt in ("-f", "--fps"):
  95. fps = int(arg)
  96. if input_file is None:
  97. print("Missing argument -i")
  98. sys.exit(2)
  99. generate(input_file, output_dir, max_samples, fps)
  100. if __name__ == "__main__":
  101. main(sys.argv[1:])