+cubanismo Posted July 11, 2020 Share Posted July 11, 2020 @JagMod, I think I've found a bug in the latest version of your cinepak tool. A good portion of the movies I encode have the audio glitch then cut out after some time. At first I thought this was just because my bitrates were too high, but it happens even when the movie is well under the required bitrate. Debugging in the cpkdemo source, I noticed audio samples are being fed to the DSP before it can even pick up & start processing the prior sample, leading some to start getting dropped. I looked at the output of the flminfo tool from the Atari tools, and normally at the settings I was using, there's an audio sample every 7-8 frames of video samples. However, near the end of the movie, I start seeing an audio sample every frame! I went and inspected the JagCinePak log files, and eventually for some reason the logic seemingly breaks down and indeed starts interleaving Audio samples in between every frame. Excerpt: MovieToFilm() #13a: Video chunks = 545, Audio chunks = 74 MovieToFilm() #13b: s = 5399, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194709 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6500, sampleTime = 194709, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 484450 MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6500, dataOffset = 34778016, s = 5400 (w) video data (size 6500) (fileoffset 0x02142408): 1019641400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5400 MovieToFilm() #13a: Video chunks = 544, Audio chunks = 74 MovieToFilm() #13b: s = 5400, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194750 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6496, sampleTime = 194750, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 484450 MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6496, dataOffset = 34784516, s = 5401 (w) video data (size 6496) (fileoffset 0x02143d6c): 1019601400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5401 MovieToFilm() #13a: Video chunks = 543, Audio chunks = 74 MovieToFilm() #13b: s = 5401, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194791 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6488, sampleTime = 194791, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 484450 MovieToFilm() #16a: GetMediaSample() = 0 MovieToFilm() #16b: audiosize = 6616, sampleTime = 4300400, num_samples_read = 6616 MovieToFilm() #18: Writing sound chunk at 4300400, 6616 samples (6616 bytes) MovieToFilm() #19a: num_samples_read (audio) = 6616, dataOffset = 34791012 MovieToFilm() #19b: incrementing chunk counter, s = 5402 (w) sound data (size 6616) (fileoffset 0x021456cc): 00000000 MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6488, dataOffset = 34797628, s = 5403 (w) video data (size 6488) (fileoffset 0x021470a4): 1019581400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5403 MovieToFilm() #13a: Video chunks = 542, Audio chunks = 73 MovieToFilm() #13b: s = 5403, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194832 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6492, sampleTime = 194832, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 477834 MovieToFilm() #16a: GetMediaSample() = 0 MovieToFilm() #16b: audiosize = 6616, sampleTime = 4307016, num_samples_read = 6616 MovieToFilm() #18: Writing sound chunk at 4307016, 6616 samples (6616 bytes) MovieToFilm() #19a: num_samples_read (audio) = 6616, dataOffset = 34804116 MovieToFilm() #19b: incrementing chunk counter, s = 5404 (w) sound data (size 6616) (fileoffset 0x021489fc): fefefdfefffefeff MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6492, dataOffset = 34810732, s = 5405 (w) video data (size 6492) (fileoffset 0x0214a3d4): 10195c1400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5405 MovieToFilm() #13a: Video chunks = 541, Audio chunks = 72 MovieToFilm() #13b: s = 5405, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194873 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6492, sampleTime = 194873, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 471218 MovieToFilm() #16a: GetMediaSample() = 0 MovieToFilm() #16b: audiosize = 6616, sampleTime = 4313632, num_samples_read = 6616 MovieToFilm() #18: Writing sound chunk at 4313632, 6616 samples (6616 bytes) MovieToFilm() #19a: num_samples_read (audio) = 6616, dataOffset = 34817224 MovieToFilm() #19b: incrementing chunk counter, s = 5406 (w) sound data (size 6616) (fileoffset 0x0214bd30): 420fcf9fafdff MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6492, dataOffset = 34823840, s = 5407 (w) video data (size 6492) (fileoffset 0x0214d708): 10195c1400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5407 MovieToFilm() #13a: Video chunks = 540, Audio chunks = 71 MovieToFilm() #13b: s = 5407, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194914 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6496, sampleTime = 194914, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 464602 MovieToFilm() #16a: GetMediaSample() = 0 MovieToFilm() #16b: audiosize = 6616, sampleTime = 4320248, num_samples_read = 6616 MovieToFilm() #18: Writing sound chunk at 4320248, 6616 samples (6616 bytes) MovieToFilm() #19a: num_samples_read (audio) = 6616, dataOffset = 34830332 MovieToFilm() #19b: incrementing chunk counter, s = 5408 (w) sound data (size 6616) (fileoffset 0x0214f064): 00000035 MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6496, dataOffset = 34836948, s = 5409 (w) video data (size 6496) (fileoffset 0x02150a3c): 1019601400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5409 MovieToFilm() #13a: Video chunks = 539, Audio chunks = 70 MovieToFilm() #13b: s = 5409, ChunkCount = 6018 ### Reading video data, m = 16892554, sampledata = 16892398, mediatime = 194955 MovieToFilm() #14a: GetMediaSample() = 0 MovieToFilm() #14b: videosize = 6492, sampleTime = 194955, durationPerSample = 41 MovieToFilm() #15: SoundMedia[0] = 17269026, SamplesRemaining = 457986 MovieToFilm() #16a: GetMediaSample() = 0 MovieToFilm() #16b: audiosize = 6616, sampleTime = 4326864, num_samples_read = 6616 MovieToFilm() #18: Writing sound chunk at 4326864, 6616 samples (6616 bytes) MovieToFilm() #19a: num_samples_read (audio) = 6616, dataOffset = 34843444 MovieToFilm() #19b: incrementing chunk counter, s = 5410 (w) sound data (size 6616) (fileoffset 0x0215239c): 00022357 MovieToFilm() #20a: Adjusting Table->Samples[] and incrmenting 's' MovieToFilm() #20b: videosize = 6492, dataOffset = 34850060, s = 5411 (w) video data (size 6492) (fileoffset 0x02153d74): 10195c1400ffffffb4 MovieToFilm() #21: At bottom of loop, s = 5411 With that understanding of the issue, if I listen very closely, I can indeed hear the DSP thread trying to cram all the remaining sound from the clip into the next few frames at this point (The "glitch"), then, presumably because of this accelerated rate of inserting samples, it quickly runs out of audio chunks and stops writing them entirely, leaving the rest of the movie to play in silence. I haven't gone and examined all the logs, but it seems to happen consistently around this time in the clips, whether I select different chunk durations (makes sense, given it seems to go wrong before converting to chunky video) or different audio interleave rates (Used 30/100ths of a second here). Seems like some calculation based on the time is overflowing or something around this point. Attaching the full MovieToFilm.log and output of flminfo -v2 for reference. BTW, if you're not still maintaining JagCinePak, or even if you are, would you mind sharing the sources? I'm doing this under WINE on Linux (Tried native windows 10 64-bit too, same behavior), but it would be great to be able to create a cross-platform toolchain or even just debug this myself. I don't care if it's ugly. Most source code is ct-1.txt MovieToFilm.log 4 Quote Link to comment Share on other sites More sharing options...
+CyranoJ Posted July 11, 2020 Share Posted July 11, 2020 Nice catch! 1 Quote Link to comment Share on other sites More sharing options...
+Saturn Posted July 13, 2020 Share Posted July 13, 2020 This is great news. Been a few years since I encoded a movie but iirc, @ovalbugmann posted having to 'slice' 5 minute movie segments into RAW chunks then chain them together for the final movie. Here, I think. Anyway, this would be epic to have fixed. Great find 1 Quote Link to comment Share on other sites More sharing options...
+cubanismo Posted July 13, 2020 Author Share Posted July 13, 2020 (edited) Yeah, I've been thinking I could write a little python script or something to repack the broken movies and verify it results in playable, correctly synced movies. Wouldn't need to completely reverse engineer the algorithm to pick how far apart they should be; just look at the pattern from the 'good' part of the file and carry it forward. The thread @Saturn references actually made me think further about the implications of this bug. It isn't limited to the audio cutting out; that's just the most obvious outcome. Audio samples are pretty big. If 8 frames worth of them are interleaved every frame, that's going to drive up the total bitrate a lot. I thought back and many of my files were previously locking up the Jaguar at around the same point the audio cut out in my latest tests. The difference? I lowered the audio sample size (lowered the audio sample length in JagCinePak) from 75/100ths of a second to 30/100ths of a second. That dramatically lowered the worst-case bitrate of my films in flminfo output and stopped the lockups, leaving me with working video but the audio glitches and silence. I didn't think too hard about why. I figured I was just getting unlucky before and an audio sample landed right next to a chunk full of keyframes or something. Now I suspect the real difference is that smaller audio samples interleaved every frame affect local bitrate less than larger audio samples interleaved every frame: I'd fixed the lockup by ensuring the data-rate bubble introduced by slamming the remainder of the video's audio samples into a handful of frames was still small enough it didn't exhaust cpkdemo's 1MB bbufferand/or the JagCD's read speed. In simpler terms, this bug could also explain movie lockups and visual anomalies at around the same 5 minute mark, not just audio drop outs. Would be great to have this fixed. Anyone know how to summon @JagMod? I also have a theory that using a smaller timescale might work around this bug somewhat. That's not a good idea for videos in general, but probably doesn't matter given there's no seeking or anything in all the Jag Cinepak uses I'm aware of. Changing the timescale is kinda hard though. I don't know if you can even do it in quicktime, or if you have to manually parse & edit the file with a script or something. Edited July 13, 2020 by cubanismo Expanded on fixer script idea, fixed weird formatting errors from mobile input 1 Quote Link to comment Share on other sites More sharing options...
Welshworrier Posted July 16, 2020 Share Posted July 16, 2020 Slight concept change for you though - it's not the sound getting out of sync with the video, it's the video getting out of sync with the sound. The sound frame is generated first to cover the upcoming number of video blobs (be that I or P frames). Somehow the conversion routines have decided that after about 194 seconds, there is only one frame of video required to fill the time space the sound occupies. Sound blobs do not have a time associated with them (that's why they have 0xffffffff attached to the time tag in the blob header). Looking at the disassembly shows a bit of signed to unsigned character maths so wouldn't be surprised if that was where the error lay. 1 Quote Link to comment Share on other sites More sharing options...
+cubanismo Posted July 16, 2020 Author Share Posted July 16, 2020 Interesting, thanks for sharing. I tried to do a little math with the timescale, fps, number of frames at around 5 minutes, and I think it worked out to around 4200000 and change, just about 1000x less than a 32-bit overflow (or 500x less than a signed overflow). Could be a red herring, or I could just be missing a factor of 1000 or 500 somewhere. Anyway, my next step is still to write a script to fix the files and verify the theory is sane and the data is otherwise good. Then if JagMod is still MIA, I'll try to extend it to a full mov->crg util on my own. My .mov parser is coming along nicely already. 1 Quote Link to comment Share on other sites More sharing options...
+cubanismo Posted July 24, 2020 Author Share Posted July 24, 2020 It turns out writing a python script to fix up the files is not quite as easy as I'd hoped ? However, I have one limping along well enough to get the job done now: https://github.com/cubanismo/cinefix This currently only works on chunky files. I've tested it successfully with quite a few so far, but the encoding & cinepak parameters used for all of them were quite similar, so more testing would be appreciated. Many fine coasters were made in the development of this script, but I've gotten one very nice Cinepak disk with no audio glitches out of it as a result so far! See the README.md in github for usage and details. I ended up deciding trying to deduce a pattern for the audio sample placement from the existing files was harder than just figuring out the algorithm for placing them from scratch. This wasn't that hard. It's all encapsulated in these two functions: def setNextAudioSampleTime(self, curSample): # XXX assumes 8-bit audio sampleDuration = (float32(curSample.size) / self.sampleRate) * self.timescale #print("Audio sample duration: " + str(sampleDuration)) if self.firstAudioSample: self.aNextTime += float32(sampleDuration) / float32(2.0) self.firstAudioSample = False else: self.aNextTime = float32(sampleDuration) + self.aNextTime def calcNextSampleType(self): if self.aNextTime < float32(self.vidTime + 1): return 'Audio' else: return 'Video' And matches the placement JagCinePak uses right up until things go wrong. I think there's room for improvement in the algorithm: It uses 32-bit floats. If I use python's built-in float type (double precision), the samples get placed slightly differently, but not necessarily incorrectly (arguably more correct). The limited precision of single-precision floating point will cause this algorithm to start to break down after about 6 minutes with a timescale of "1000", nearly twice as long with a better timescape of "600". It probably still works OK, but the samples will start to bounce around more and more. I'll try to do some testing (going through a lot of CD-R's here) with the higher-precision version, and also try to revive a version I had that worked in relative units and hence kept the magnitude in check better and switch over to that, or implement the Q32.16 fixed-point math that cinepak uses for constant accuracy regardless of the time. Also, obviously, I should add support for different audio sample resolutions ? The script also *almost* supports fixing smooth files, but I sprinted to the end with chunky files because that's all I care about at the moment, and probably all anyone cares about for videos longer than ~10 seconds. All the sample iterator code should work with both types, I just need to clean up the fixed file generator logic to output a smooth file as well. At that point, the script could easily be modified to convert to/from chunky/smooth as well. I was hoping it would be obvious why JagCinePak falls over near the ~5 minute mark, but it's still not. As noted, the precision isn't the best beyond this point, but I don't know why it completely falls apart. Still hoping @JagMod will show up and figure that out ? 3 Quote Link to comment Share on other sites More sharing options...
JagMod Posted July 30, 2020 Share Posted July 30, 2020 JagCinePak.exe version 1.53 07/29/20 ===================================== Fixed 32bit overflow. Longer movies with high audio sample rates cause the sample count to exceed 2^32 Thanks to Cubanismo for finding this bug. Download new version of JagCinepak 7 2 Quote Link to comment Share on other sites More sharing options...
Fredifredo Posted July 30, 2020 Share Posted July 30, 2020 So, now the big question : Any chance to have a Jaguar Cinepak version for Jaguar GameDrive ? 2 Quote Link to comment Share on other sites More sharing options...
+Saturn Posted July 30, 2020 Share Posted July 30, 2020 8 hours ago, JagMod said: JagCinePak.exe version 1.53 07/29/20 ===================================== Fixed 32bit overflow. Longer movies with high audio sample rates cause the sample count to exceed 2^32 Thanks to Cubanismo for finding this bug. Download new version of JagCinepak Great, can't wait to test this out! Hopefully no more split segments into 5 minute chunks. Quote Link to comment Share on other sites More sharing options...
+cubanismo Posted July 30, 2020 Author Share Posted July 30, 2020 4 hours ago, Fredifredo said: So, now the big question : Any chance to have a Jaguar Cinepak version for Jaguar GameDrive ? Are the bank switching mechanisms documented yet? It should be a pretty easy modification to cpkdemo, but I've no way to test right now. Of course, if CD emu support is working, the current code should work fine. Quote Link to comment Share on other sites More sharing options...
Songbird Posted July 30, 2020 Share Posted July 30, 2020 Also, how does one view a Cinepak file on a mostly modern Windows PC? Is there a tool to convert a file back into (for example) MOV or AVI? Quote Link to comment Share on other sites More sharing options...
JagMod Posted July 30, 2020 Share Posted July 30, 2020 Not sure why you would want to convert back, since Cinepak is a lossy video codec. Quote Link to comment Share on other sites More sharing options...
Songbird Posted July 30, 2020 Share Posted July 30, 2020 Ha! I'm looking for a method to view extracted Cinepak videos on a PC to confirm they were properly extracted. Quote Link to comment Share on other sites More sharing options...
JagMod Posted July 30, 2020 Share Posted July 30, 2020 You could write a program to query/validate the format of the extracted Cinepak videos . * In the chunky format, the film is broken up into temporal chunks of a * specified duration. A chunk table near the start of the film gives the * offsets to all chunk records which follow. Every chunk record begins * with a sync pattern and contains a sample table giving the offsets * relative to the base of the chunk of the audio and video data included * within the chunk. Quote Link to comment Share on other sites More sharing options...
+cubanismo Posted July 30, 2020 Author Share Posted July 30, 2020 (edited) 26 minutes ago, JagMod said: You could write a program to query/validate the format of the extracted Cinepak videos If you want to see some code that parses chunky & smooth cinepak files, look at my cinefix script above. If you're extracting cinepak from a cinepak-only disk using the cpkdemo player everyone has used, it'll have a bunch of AIFF and track header junk on it. Just scan forward from the start of the file until you find the 64-byte sync header of ASCII 1's, and then start reading the film/frame header structure. Again, see the cinefix code that re-wraps the cinepak file in these headers for details. If it's some other source (e.g., Battlemorph) you'd have to eyeball the tracks/files in a hex editor first to see if any of the extra padding is present. Quicktime .mov is a moderately more complicated container to write out than the Jaguar smooth/chunky files, but it probably wouldn't be too hard to add code to dump a .mov back out using the sample iterator and header parsing classes in cinefix. Converting to/from each container type should be a lossless operation (sample rate of the sound might be slightly perturbed at worst), so it'd end up no worse than your already-cinepak-encoded source material. Edited July 30, 2020 by cubanismo typos, add note on non-cpkdemo vids 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.