Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vi committed Apr 28, 2016
1 parent 393f5ce commit b73c8d7
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 128 deletions.
4 changes: 4 additions & 0 deletions Makefile
@@ -0,0 +1,4 @@
all: mkvsplit

mkvsplit: mkvsplit.o
${CC} ${LDFLAGS} mkvsplit.o -o mkvsplit
57 changes: 49 additions & 8 deletions mkvcat.py
Expand Up @@ -3,6 +3,9 @@
import sys
import traceback
import binascii
import os
import io
import mkvgen

def pstderr(*args):
#if sys.version < '3':
Expand All @@ -13,6 +16,11 @@ def pstderr(*args):
class MatroskaConcatenator(mkvparse.MatroskaHandler):
def __init__(self, f):
self.f = f
self.prev_uuid = None
self.cur_uuid = os.urandom(16)
self.next_uuid = os.urandom(16)
self.headerpart = None
self.info_section_size = None

def tracks_available(self):
pstderr(" Tracks info:")
Expand All @@ -37,28 +45,61 @@ def frame(self, track_id, timestamp, data, more_laced_frames, duration, keyframe


def before_handling_an_element(self):
self.headerpart = self.fin.peek(100)
self.headerpart = self.fin.peek(100)
pstderr("peeked %d bytes"%(len(self.headerpart)))
pass

def begin_handling_ebml_element(self, id_, name, type_, headersize, datasize):
pstderr(" %s %d %d"%(name, headersize, datasize))
if type_==1:
return 'read tree'
return 'pass'
pstderr(" %s %d(%s) %d"%(name, headersize, binascii.hexlify(self.headerpart[:headersize]), datasize))
if name=="EBML": return type_
elif name=="Segment":
remainingfilesize = self.currentfilesize - self.fin.tell()
pstderr(" remaining file size: %d segment data size: %s"%(remainingfilesize, str(datasize)))
if datasize <= 0 and remainingfilesize > 0:
datasize = remainingfilesize
pstderr(" fixed segment size based on file size")
self.f.write(mkvgen.ebml_element(0x18538067,"",datasize)) # Segment header with known length

return mkvparse.EbmlElementType.JUST_GO_ON
#elif name=="Info":
# self.info_section_size = datasize
# return mkvparse.EbmlElementType.JUST_GO_ON

self.headersize = headersize
return mkvparse.EbmlElementType.BINARY

def element_data_available(self, id_, name, type, headersize, data):
def ebml_top_element(self, id_, name, type_, data):
#pstderr(" %s %d %d"%(name, headersize, data))
if name=='EBML': return 'read tree'
if name=="EBML": return
elif name=="Segment": return
#elif name=="Info": return
#elif name=="SeekHead": return # invalidated by changing the Info

#pstderr("type:"+str(type(data)))

# just copy this element to output
#header = self.headerpart [ : self.headersize];
#pstderr("writing header: %s at %s; data len: %d"%(binascii.hexlify(header), self.f.tell(), len(data)))
#self.f.write(header)
#self.f.write(data)
self.f.write(mkvgen.ebml_element(id_,data))


if __name__ == '__main__':
if sys.version >= '3':
sys.stdout = sys.stdout.detach()

mkvgen.write_ebml_header(sys.stdout, "matroska", 2, 2)

concat = MatroskaConcatenator(sys.stdout)

for i in sys.argv[1:]:
pstderr("Opening %s" % i);
with open(i,"rb") as f:
with io.open(i,"rb", buffering=True) as f:
try:
mkvparse.mkvparse(f, concat)
concat.fin = f
concat.currentfilesize = os.path.getsize(i)
mkvparse.mkvparse(concat.fin, concat)
except:
traceback.print_exc()
210 changes: 109 additions & 101 deletions mkvgen.py
Expand Up @@ -50,131 +50,139 @@ def ebml_element(element_id, data, length=None):
length = len(data)
return big_endian_number(element_id) + ebml_encode_number(length) + data

# Write EBML header
sys.stdout.write(
ebml_element(0x1A45DFA3, "" # EBML
+ ebml_element(0x4286, ben(1)) # EBMLVersion
+ ebml_element(0x42F7, ben(1)) # EBMLReadVersion
+ ebml_element(0x42F2, ben(4)) # EBMLMaxIDLength
+ ebml_element(0x42F3, ben(8)) # EBMLMaxSizeLength
+ ebml_element(0x4282, "matroska") # DocType
+ ebml_element(0x4287, ben(2)) # DocTypeVersion
+ ebml_element(0x4285, ben(2)) # DocTypeReadVersion
))

# write segment element header
sys.stdout.write(ebml_element(0x18538067,"",-1)) # Segment (unknown length)
def write_ebml_header(f, content_type, version, read_version):
f.write(
ebml_element(0x1A45DFA3, "" # EBML
+ ebml_element(0x4286, ben(1)) # EBMLVersion
+ ebml_element(0x42F7, ben(1)) # EBMLReadVersion
+ ebml_element(0x42F2, ben(4)) # EBMLMaxIDLength
+ ebml_element(0x42F3, ben(8)) # EBMLMaxSizeLength
+ ebml_element(0x4282, content_type) # DocType
+ ebml_element(0x4287, ben(version)) # DocTypeVersion
+ ebml_element(0x4285, ben(read_version)) # DocTypeReadVersion
))

def write_infinite_segment_header(f):
# write segment element header
f.write(ebml_element(0x18538067,"",-1)) # Segment (unknown length)

def random_uid():
def rint():
return int(random.random()*(0x100**4))
return ben(rint()) + ben(rint()) + ben(rint()) + ben(rint())


def example():
write_ebml_header(sys.stdout, "matroska", 2, 2)
write_infinite_segment_header(sys.stdout)


# write segment info (optional)
sys.stdout.write(ebml_element(0x1549A966, "" # SegmentInfo
+ ebml_element(0x73A4, random_uid()) # SegmentUID
+ ebml_element(0x7BA9, "mkvgen.py test") # Title
+ ebml_element(0x4D80, "mkvgen.py") # MuxingApp
+ ebml_element(0x5741, "mkvgen.py") # WritingApp
))

# write trans data (codecs etc.)
sys.stdout.write(ebml_element(0x1654AE6B, "" # Tracks
+ ebml_element(0xAE, "" # TrackEntry
+ ebml_element(0xD7, ben(1)) # TrackNumber
+ ebml_element(0x73C5, ben(0x77)) # TrackUID
+ ebml_element(0x83, ben(0x01)) # TrackType
#0x01 track is a video track
#0x02 track is an audio track
#0x03 track is a complex track, i.e. a combined video and audio track
#0x10 track is a logo track
#0x11 track is a subtitle track
#0x12 track is a button track
#0x20 track is a control track
+ ebml_element(0x536E, "mjpeg data") # Name
+ ebml_element(0x86, "V_MJPEG") # CodecID
#+ ebml_element(0x23E383, ben(100000000)) # DefaultDuration (opt.), nanoseconds
#+ ebml_element(0x6DE7, ben(100)) # MinCache
+ ebml_element(0xE0, "" # Video
+ ebml_element(0xB0, ben(640)) # PixelWidth
+ ebml_element(0xBA, ben(480)) # PixelHeight
# write segment info (optional)
sys.stdout.write(ebml_element(0x1549A966, "" # SegmentInfo
+ ebml_element(0x73A4, random_uid()) # SegmentUID
+ ebml_element(0x7BA9, "mkvgen.py test") # Title
+ ebml_element(0x4D80, "mkvgen.py") # MuxingApp
+ ebml_element(0x5741, "mkvgen.py") # WritingApp
))

# write trans data (codecs etc.)
sys.stdout.write(ebml_element(0x1654AE6B, "" # Tracks
+ ebml_element(0xAE, "" # TrackEntry
+ ebml_element(0xD7, ben(1)) # TrackNumber
+ ebml_element(0x73C5, ben(0x77)) # TrackUID
+ ebml_element(0x83, ben(0x01)) # TrackType
#0x01 track is a video track
#0x02 track is an audio track
#0x03 track is a complex track, i.e. a combined video and audio track
#0x10 track is a logo track
#0x11 track is a subtitle track
#0x12 track is a button track
#0x20 track is a control track
+ ebml_element(0x536E, "mjpeg data") # Name
+ ebml_element(0x86, "V_MJPEG") # CodecID
#+ ebml_element(0x23E383, ben(100000000)) # DefaultDuration (opt.), nanoseconds
#+ ebml_element(0x6DE7, ben(100)) # MinCache
+ ebml_element(0xE0, "" # Video
+ ebml_element(0xB0, ben(640)) # PixelWidth
+ ebml_element(0xBA, ben(480)) # PixelHeight
)
)
)
+ ebml_element(0xAE, "" # TrackEntry
+ ebml_element(0xD7, ben(2)) # TrackNumber
+ ebml_element(0x73C5, ben(0x78)) # TrackUID
+ ebml_element(0x83, ben(0x02)) # TrackType
#0x01 track is a video track
#0x02 track is an audio track
#0x03 track is a complex track, i.e. a combined video and audio track
#0x10 track is a logo track
#0x11 track is a subtitle track
#0x12 track is a button track
#0x20 track is a control track
+ ebml_element(0x536E, "content of mp3 file") # Name
#+ ebml_element(0x6DE7, ben(100)) # MinCache
+ ebml_element(0x86, "A_MPEG/L3") # CodecID
#+ ebml_element(0xE1, "") # Audio
)
))


mp3file = open("q.mp3", "rb")
mp3file.read(500000);

def mp3framesgenerator(f):
debt=""
while True:
for i in xrange(0,len(debt)+1):
if i >= len(debt)-1:
debt = debt + f.read(8192)
break
#sys.stderr.write("i="+str(i)+" len="+str(len(debt))+"\n")
if ord(debt[i])==0xFF and (ord(debt[i+1]) & 0xF0)==0XF0 and i>700:
if i>0:
yield debt[0:i]
# sys.stderr.write("len="+str(i)+"\n")
debt = debt[i:]
break

+ ebml_element(0xAE, "" # TrackEntry
+ ebml_element(0xD7, ben(2)) # TrackNumber
+ ebml_element(0x73C5, ben(0x78)) # TrackUID
+ ebml_element(0x83, ben(0x02)) # TrackType
#0x01 track is a video track
#0x02 track is an audio track
#0x03 track is a complex track, i.e. a combined video and audio track
#0x10 track is a logo track
#0x11 track is a subtitle track
#0x12 track is a button track
#0x20 track is a control track
+ ebml_element(0x536E, "content of mp3 file") # Name
#+ ebml_element(0x6DE7, ben(100)) # MinCache
+ ebml_element(0x86, "A_MPEG/L3") # CodecID
#+ ebml_element(0xE1, "") # Audio
)
))


mp3 = mp3framesgenerator(mp3file)
mp3.next()
mp3file = open("q.mp3", "rb")
mp3file.read(500000);

def mp3framesgenerator(f):
debt=""
while True:
for i in xrange(0,len(debt)+1):
if i >= len(debt)-1:
debt = debt + f.read(8192)
break
#sys.stderr.write("i="+str(i)+" len="+str(len(debt))+"\n")
if ord(debt[i])==0xFF and (ord(debt[i+1]) & 0xF0)==0XF0 and i>700:
if i>0:
yield debt[0:i]
# sys.stderr.write("len="+str(i)+"\n")
debt = debt[i:]
break


for i in xrange(0,530):
framefile = open("img/"+str(i)+".jpg", "rb")
framedata = framefile.read()
framefile.close()
mp3 = mp3framesgenerator(mp3file)
mp3.next()

# write cluster (actual video data)

if random.random()<1:
sys.stdout.write(ebml_element(0x1F43B675, "" # Cluster
+ ebml_element(0xE7, ben(int(i*26*4))) # TimeCode, uint, milliseconds
# + ebml_element(0xA7, ben(0)) # Position, uint
+ ebml_element(0xA3, "" # SimpleBlock
+ ebml_encode_number(1) # track number
+ chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds
+ chr(0x00) # flags
+ framedata
)))
for i in xrange(0,530):
framefile = open("img/"+str(i)+".jpg", "rb")
framedata = framefile.read()
framefile.close()

# write cluster (actual video data)

for u in xrange(0,4):
mp3f=mp3.next()
if random.random()<1:
sys.stdout.write(ebml_element(0x1F43B675, "" # Cluster
+ ebml_element(0xE7, ben(i*26*4+u*26)) # TimeCode, uint, milliseconds
+ ebml_element(0xE7, ben(int(i*26*4))) # TimeCode, uint, milliseconds
# + ebml_element(0xA7, ben(0)) # Position, uint
+ ebml_element(0xA3, "" # SimpleBlock
+ ebml_encode_number(2) # track number
+ ebml_encode_number(1) # track number
+ chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds
+ chr(0x00) # flags
+ mp3f
+ framedata
)))

for u in xrange(0,4):
mp3f=mp3.next()
if random.random()<1:
sys.stdout.write(ebml_element(0x1F43B675, "" # Cluster
+ ebml_element(0xE7, ben(i*26*4+u*26)) # TimeCode, uint, milliseconds
+ ebml_element(0xA3, "" # SimpleBlock
+ ebml_encode_number(2) # track number
+ chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds
+ chr(0x00) # flags
+ mp3f
)))





if __name__ == '__main__':
example()

0 comments on commit b73c8d7

Please sign in to comment.