-
Notifications
You must be signed in to change notification settings - Fork 4
/
dvd_time.c
269 lines (210 loc) · 9.09 KB
/
dvd_time.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#include "dvd_time.h"
/**
* Convert any value of dvd_time to milliseconds -- uses the same code set as
* lsdvd for compatability.
*
* Regarding the bit-shifting in the function, I've broken down the
* calculations to separate individual time metrics to make it a bit easier to
* read, though I admittedly don't know enough about bit shifting to know what
* it's doing or why it doesn't just do simple calculation. It's worth seeing
* ifo_print_time() from libdvdread as a comparative reference as well.
*
* Old notes:
*
* Though it is not implemented here, I have a theory that msecs should be a
* floating value to a decimal point of 2, and that the framerate should be
* a whole integer (30) instead of a floating point decimal (29.97). The
* rest of these notes explain that approach, thoough it is not used. They
* are kept here for historical purposes for my own benefit.
*
* Originally, the title track length was calculated by looking at the PGC for
* the track itself. With that approach, the total length for the track did
* not match the sum of the total length of all the individual cells. The
* offset was small, in the range of -5 to +5 *milliseconds* and it did not
* occur all the time. To fix the title track length, I changed it to use the
* total from all the cells. That by itself changed the track lengths to msec
* offsets that looked a little strange from their original parts. Fex, 500
* would change to 501, 900 to 898, etc. Since it's far more likely that the
* correct value was a whole integer (.500 seconds happens all the time), then
* it needed to be changed.
*
* According to http://stnsoft.com/DVD/pgc.html it looks like the FPS values are
* integers, either 25 or 30. So this uses 3000 instead of 2997 for that reason.
* as well. With these two changes only does minor changes, and they are always
* something like 734 to 733, 667 to 666, 834 to 833, and so on.
*
* Either way, it's probably safe to say that calculating the exact length of a
* cell or track is hard, and that this is the best approxmiation that
* preserves what the original track length is estimated to be from the PGC. So
* this method is used as a preference, even though I don't necessarily want to
* recommend relying on either approach for complete accuracy.
*
* Making these changes though is going to differ in displaying the track
* length from other DVD applications -- again, in milliseconds only -- but I
* justify this approach in the sense that using this way, the cell, chapter
* and title track lengths all match up.
*/
uint32_t dvd_time_to_milliseconds(dvd_time_t *dvd_time) {
int i = (dvd_time->frame_u & 0xc0) >> 6;
if(i < 0 || i > 3)
return 0;
uint32_t framerates[4] = {0, 2500, 0, 2997};
uint32_t framerate = framerates[i];
uint32_t hours = (((dvd_time->hour & 0xf0) >> 3) * 5 + (dvd_time->hour & 0x0f));
uint32_t minutes = (((dvd_time->minute & 0xf0) >> 3) * 5 + (dvd_time->minute & 0x0f));
uint32_t seconds = (((dvd_time->second & 0xf0) >> 3) * 5 + (dvd_time->second & 0x0f));
uint32_t msecs = 0;
if(framerate > 0)
msecs = ((((dvd_time->frame_u & 0x30) >> 3) * 5 + (dvd_time->frame_u & 0x0f)) * 100000) / framerate;
uint32_t total = (hours * 3600000);
total += (minutes * 60000);
total += (seconds * 1000);
total += msecs;
return total;
// For reference, here is how lsdvd's dvdtime2msec function works
/*
double framerates[4] = {-1.0, 25.00, -1.0, 29.97};
int i = (dvd_time->frame_u & 0xc0) >> 6;
double framerate = framerates[i];
uint32_t msecs = 0;
msecs = (((dvd_time->hour & 0xf0) >> 3) * 5 + (dvd_time->hour & 0x0f)) * 3600000;
msecs += (((dvd_time->minute & 0xf0) >> 3) * 5 + (dvd_time->minute & 0x0f)) * 60000;
msecs += (((dvd_time->second & 0xf0) >> 3) * 5 + (dvd_time->second & 0x0f)) * 1000;
if(framerate > 0)
msecs += (((dvd_time->frame_u & 0x30) >> 3) * 5 + (dvd_time->frame_u & 0x0f)) * 1000.0 / framerate;
return msecs;
*/
}
/**
* Convert milliseconds to format hh:mm:ss.ms
*/
void milliseconds_length_format(char *dest_str, uint32_t milliseconds) {
uint32_t total_seconds = milliseconds / 1000;
uint32_t hours = total_seconds / (3600);
uint32_t minutes = (total_seconds / 60) % 60;
if(minutes > 59)
minutes -= 59;
uint32_t seconds = total_seconds % 60;
if(seconds > 59)
seconds -= 59;
uint32_t msecs = milliseconds - (hours * 3600 * 1000) - (minutes * 60 * 1000) - (seconds * 1000);
memset(dest_str, '\0', 13);
sprintf(dest_str, "%02u:%02u:%02u.%03u", hours, minutes, seconds, msecs);
}
/**
* Get the number of milliseconds for a title track using the program chain.
*
*/
uint32_t dvd_track_msecs(ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number) {
if(vts_ifo->vts_pgcit == NULL)
return 0;
uint8_t ttn = dvd_track_ttn(vmg_ifo, track_number);
pgcit_t *vts_pgcit = vts_ifo->vts_pgcit;
pgc_t *pgc = vts_pgcit->pgci_srp[vts_ifo->vts_ptt_srpt->title[ttn - 1].ptt[0].pgcn - 1].pgc;
if(pgc == NULL || pgc->cell_playback == NULL)
return 0;
uint32_t msecs = dvd_time_to_milliseconds(&pgc->playback_time);
return msecs;
}
/**
* OLD NOTES, not relevant any more since it calculates all cell lengths:
* Sourced from lsdvd.c. I don't understand the logic behind it, and why the
* original doesn't access pgc->cell_playback[cell_ix].playback_time directly.
* Two things I do know: not to assume a cell is a chapter, and lsdvd's chapter
* output has always worked for me, so I'm leaving it alone for now.
*
* This loops through *all* the chapters and gets the times, but only quits
* once the specified one has been found.
* END OLD NOTES
*/
/**
* Get the number of milliseconds of a chapter
*/
uint32_t dvd_chapter_msecs(ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number, uint8_t chapter_number) {
if(vts_ifo->vts_pgcit == NULL)
return 0;
uint8_t ttn = dvd_track_ttn(vmg_ifo, track_number);
pgcit_t *vts_pgcit = vts_ifo->vts_pgcit;
pgc_t *pgc = vts_pgcit->pgci_srp[vts_ifo->vts_ptt_srpt->title[ttn - 1].ptt[0].pgcn - 1].pgc;
if(pgc->cell_playback == NULL || pgc->program_map == NULL)
return 0;
uint8_t chapters = pgc->nr_of_programs;
uint8_t chapter_idx = 0;
uint8_t program_map_ix = 0;
uint8_t cell_ix = 0;
uint32_t msecs = 0;
for(chapter_idx = 0; chapter_idx < chapters; chapter_idx++) {
if(chapter_idx == chapters - 1)
program_map_ix = pgc->nr_of_cells + 1;
else
program_map_ix = pgc->program_map[chapter_idx + 1];
while(cell_ix < program_map_ix - 1) {
if(chapter_idx + 1 == chapter_number) {
msecs += dvd_time_to_milliseconds(&pgc->cell_playback[cell_ix].playback_time);
}
cell_ix++;
}
}
return msecs;
}
/**
* Get the number of milliseconds of a cell
*/
uint32_t dvd_cell_msecs(ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number, uint8_t cell_number) {
if(vts_ifo->vts_pgcit == NULL)
return 0;
uint8_t ttn = dvd_track_ttn(vmg_ifo, track_number);
pgcit_t *vts_pgcit = vts_ifo->vts_pgcit;
pgc_t *pgc = vts_pgcit->pgci_srp[vts_ifo->vts_ptt_srpt->title[ttn - 1].ptt[0].pgcn - 1].pgc;
if(pgc->cell_playback == NULL)
return 0;
uint32_t msecs = dvd_time_to_milliseconds(&pgc->cell_playback[cell_number - 1].playback_time);
return msecs;
}
/**
* Get the number of milliseconds for a title track by totalling the value of
* all chapter length.
*
* This function is to be used for debugging or development. The total msecs
* of a title track should obviously be the same total of the length of all
* the chapters (and cells) that add up, but this is not the case. It is
* often off by a few milliseconds (-5 to +5), but in some rare cases it can
* be a total of minutes.
*
* The original dvd_track_msecs() function looks at the program chain for the
* total, and that method is used across the board for pretty much every DVD
* application that uses libdvdread. So, this is part of dvd_info only, and
* is used to flag anomalies using the dvd_debug program.
*
*/
uint32_t dvd_track_total_chapter_msecs(ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number) {
uint8_t chapters = dvd_track_chapters(vmg_ifo, vts_ifo, track_number);
if(chapters == 0)
return 0;
uint32_t msecs = 0;
uint8_t chapter;
for(chapter = 1; chapter <= chapters; chapter++)
msecs += dvd_chapter_msecs(vmg_ifo, vts_ifo, track_number, chapter);
return msecs;
}
/**
* Get the formatted string length of a title track
*/
void dvd_track_length(char *dest_str, ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number) {
uint32_t msecs = dvd_track_msecs(vmg_ifo, vts_ifo, track_number);
milliseconds_length_format(dest_str, msecs);
}
/**
* Get the formatted string length of a chapter
*/
void dvd_chapter_length(char *dest_str, ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number, uint8_t chapter_number) {
uint32_t msecs = dvd_chapter_msecs(vmg_ifo, vts_ifo, track_number, chapter_number);
milliseconds_length_format(dest_str, msecs);
}
/**
* Get the formatted string length of a cell
*/
void dvd_cell_length(char *dest_str, ifo_handle_t *vmg_ifo, ifo_handle_t *vts_ifo, uint16_t track_number, uint8_t cell_number) {
uint32_t msecs = dvd_cell_msecs(vmg_ifo, vts_ifo, track_number, cell_number);
milliseconds_length_format(dest_str, msecs);
}