1 /*
2  * This file is part of cyanrip.
3  *
4  * cyanrip is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * cyanrip is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with cyanrip; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <stdarg.h>
20 #include <time.h>
21 #include <pthread.h>
22 
23 #include <libavutil/avutil.h>
24 
25 #include "cyanrip_encode.h"
26 #include "cyanrip_log.h"
27 #include "accurip.h"
28 
29 #define CLOG(FORMAT, DICT, TAG)                                                \
30     if (dict_get(DICT, TAG))                                                   \
31         cyanrip_log(ctx, 0, FORMAT, dict_get(DICT, TAG));                      \
32 
print_offsets(cyanrip_ctx * ctx,cyanrip_track * t)33 static void print_offsets(cyanrip_ctx *ctx, cyanrip_track *t)
34 {
35     if (t->pregap_lsn != CDIO_INVALID_LSN)
36         cyanrip_log(ctx, 0, "    Pregap LSN:       %i\n", t->pregap_lsn);
37 
38     if (t->frames_before_disc_start)
39         cyanrip_log(ctx, 0, "    Silent frames:    %i prepended\n", t->frames_before_disc_start);
40     cyanrip_log(ctx, 0, "    Start LSN:        %i\n", t->start_lsn_sig);
41     if (t->start_lsn != t->start_lsn_sig)
42         cyanrip_log(ctx, 0, "    Offset start:     %i\n", t->start_lsn);
43 
44     cyanrip_log(ctx, 0, "    End LSN:          %i\n", t->end_lsn_sig);
45     if (t->end_lsn != t->end_lsn_sig)
46         cyanrip_log(ctx, 0, "    Offset end:       %i\n", t->end_lsn);
47     if (t->frames_after_disc_end)
48         cyanrip_log(ctx, 0, "    Silent frames:    %i appended\n", t->frames_after_disc_end);
49 }
50 
cyanrip_log_track_end(cyanrip_ctx * ctx,cyanrip_track * t)51 void cyanrip_log_track_end(cyanrip_ctx *ctx, cyanrip_track *t)
52 {
53     char length[16];
54     cyanrip_samples_to_duration(t->nb_samples, length);
55 
56     if (t->track_is_data) {
57         cyanrip_log(ctx, 0, "    Data bytes:       %i (%.2f Mib)\n",
58                     t->frames*CDIO_CD_FRAMESIZE_RAW,
59                     t->frames*CDIO_CD_FRAMESIZE_RAW / (1024.0 * 1024.0));
60         cyanrip_log(ctx, 0, "    Frames:           %u\n", t->end_lsn_sig - t->start_lsn_sig + 1);
61         print_offsets(ctx, t);
62         cyanrip_log(ctx, 0, "\n");
63         return;
64     }
65 
66     cyanrip_log(ctx, 0, "    File(s):\n");
67     for (int f = 0; f < ctx->settings.outputs_num; f++) {
68         char *path = crip_get_path(ctx, CRIP_PATH_TRACK, 0,
69                                    &crip_fmt_info[ctx->settings.outputs[f]],
70                                    t);
71         cyanrip_log(ctx, 0, "        %s\n", path);
72         av_free(path);
73     }
74 
75     cyanrip_log(ctx, 0, "    Metadata:\n", length);
76 
77     const AVDictionaryEntry *d = NULL;
78     while ((d = av_dict_get(t->meta, "", d, AV_DICT_IGNORE_SUFFIX)))
79         cyanrip_log(ctx, 0, "        %s: %s\n", d->key, d->value);
80 
81     if (t->preemphasis)
82         cyanrip_log(ctx, 0, "    Preemphasis:      present, deemphasis required\n");
83     if (t->art.source_url || ctx->nb_cover_arts) {
84         const char *codec_name = NULL;
85         CRIPArt *art = &t->art;
86         if (!art->source_url) {
87             int i;
88             for (i = 0; i < ctx->nb_cover_arts; i++)
89                 if (!strcmp(dict_get(ctx->cover_arts[i].meta, "title"), "Front"))
90                     break;
91             art = &ctx->cover_arts[i == ctx->nb_cover_arts ? 0 : i];
92         }
93 
94         if (art->pkt && art->params) {
95             const AVCodecDescriptor *cd = avcodec_descriptor_get(art->params->codec_id);
96             if (cd)
97                 codec_name = cd->long_name;
98             else
99                 codec_name = avcodec_get_name(art->params->codec_id);
100         }
101 
102         cyanrip_log(ctx, 0, "    Cover art:        %s\n",
103                     codec_name ? codec_name :
104                     (t->art.source_url ? art->source_url : dict_get(art->meta, "title")));
105     }
106     cyanrip_log(ctx, 0, "    Duration:         %s\n", length);
107     cyanrip_log(ctx, 0, "    Samples:          %u\n", t->nb_samples);
108     cyanrip_log(ctx, 0, "    Frames:           %u\n", t->end_lsn_sig - t->start_lsn_sig + 1);
109 
110     print_offsets(ctx, t);
111 
112     int has_ar = t->ar_db_status == CYANRIP_ACCUDB_FOUND;
113 
114     if (!ctx->settings.disable_accurip) {
115         cyanrip_log(ctx, 0, "    Accurip:          %s", has_ar ? "found" : "not found");
116         if (has_ar)
117             cyanrip_log(ctx, 0, " (max confidence: %i)\n", t->ar_db_max_confidence);
118         else
119             cyanrip_log(ctx, 0, "\n");
120     }
121 
122     if (t->computed_crcs) {
123         cyanrip_log(ctx, 0, "    EAC CRC32:        %08X\n", t->eac_crc);
124 
125         int match_v1 = has_ar ? crip_find_ar(t, t->acurip_checksum_v1, 0) : 0;
126         int match_v2 = has_ar ? crip_find_ar(t, t->acurip_checksum_v2, 0) : 0;
127 
128         cyanrip_log(ctx, 0, "    Accurip v1:       %08X", t->acurip_checksum_v1);
129         if (has_ar && match_v1 > 0)
130             cyanrip_log(ctx, 0, " (accurately ripped, confidence %i)\n", match_v1);
131         else if (has_ar && (match_v2 < 1))
132             cyanrip_log(ctx, 0, " (not found in the Accurip database)\n");
133         else
134             cyanrip_log(ctx, 0, "\n");
135 
136         cyanrip_log(ctx, 0, "    Accurip v2:       %08X", t->acurip_checksum_v2);
137         if (has_ar && (match_v2 > 0))
138             cyanrip_log(ctx, 0, " (accurately ripped, confidence %i)\n", match_v2);
139         else if (has_ar && (match_v1 < 0))
140             cyanrip_log(ctx, 0, " (not found in the Accurip database)\n");
141         else
142             cyanrip_log(ctx, 0, "\n");
143 
144         if (!has_ar || ((match_v1 < 0) && (match_v2 < 0))) {
145             int match_450 = has_ar ? crip_find_ar(t, t->acurip_checksum_v1_450, 1) : 0;
146             cyanrip_log(ctx, 0, "    Accurip v1 450:   %08X", t->acurip_checksum_v1_450);
147             if (has_ar && (match_450 > (3*(t->ar_db_max_confidence+1)/4)) && (t->acurip_checksum_v1_450 == 0x0))
148                 cyanrip_log(ctx, 0, " (match found, confidence %i, but a checksum of 0 is meaningless)\n",
149                             match_450, t->ar_db_max_confidence);
150             else if (has_ar && (match_450 > (3*(t->ar_db_max_confidence+1)/4)))
151                 cyanrip_log(ctx, 0, " (matches Accurip DB, confidence %i, track is partially accurately ripped)\n",
152                             match_450, t->ar_db_max_confidence);
153             else if (has_ar)
154                 cyanrip_log(ctx, 0, " (not found in the Accurip database)\n");
155             else
156                 cyanrip_log(ctx, 0, "\n");
157         }
158     }
159 
160     cyanrip_log(ctx, 0, "\n");
161 }
162 
cyanrip_log_start_report(cyanrip_ctx * ctx)163 void cyanrip_log_start_report(cyanrip_ctx *ctx)
164 {
165     cyanrip_log(ctx, 0, "cyanrip %s (%s)\n", PROJECT_VERSION_STRING, vcstag);
166     cyanrip_log(ctx, 0, "System device:  %s\n", ctx->settings.dev_path);
167     if (ctx->drive->drive_model)
168         cyanrip_log(ctx, 0, "Device model:   %s\n", ctx->drive->drive_model);
169     cyanrip_log(ctx, 0, "Offset:         %c%i %s\n", ctx->settings.offset >= 0 ? '+' : '-', abs(ctx->settings.offset),
170                 abs(ctx->settings.offset) == 1 ? "sample" : "samples");
171     cyanrip_log(ctx, 0, "%s%c%i %s\n",
172                 ctx->settings.over_under_read_frames < 0 ? "Underread:      " : "Overread:       ",
173                 ctx->settings.over_under_read_frames >= 0 ? '+' : '-',
174                 abs(ctx->settings.over_under_read_frames),
175                 abs(ctx->settings.over_under_read_frames) == 1 ? "frame" : "frames");
176     cyanrip_log(ctx, 0, "%s%s\n",
177                 ctx->settings.over_under_read_frames < 0 ? "Underread mode: " : "Overread mode:  ",
178                 ctx->settings.overread_leadinout ? "read in lead-in/lead-out" : "fill with silence in lead-in/lead-out");
179     if (ctx->settings.speed && (ctx->mcap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED))
180         cyanrip_log(ctx, 0, "Speed:          %ix\n", ctx->settings.speed);
181     else
182         cyanrip_log(ctx, 0, "Speed:          default (%s)\n",
183                     (ctx->mcap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED) ? "changeable" : "unchangeable");
184     cyanrip_log(ctx, 0, "C2 errors:      %s by drive\n", (ctx->rcap & CDIO_DRIVE_CAP_READ_C2_ERRS) ?
185                 "supported" : "unsupported");
186     if (ctx->settings.paranoia_level == crip_max_paranoia_level)
187         cyanrip_log(ctx, 0, "Paranoia level: %s\n", "max");
188     else if (ctx->settings.paranoia_level == 0)
189         cyanrip_log(ctx, 0, "Paranoia level: %s\n", "none");
190     else
191         cyanrip_log(ctx, 0, "Paranoia level: %i\n", ctx->settings.paranoia_level);
192     cyanrip_log(ctx, 0, "Frame retries:  %i\n", ctx->settings.frame_max_retries);
193     cyanrip_log(ctx, 0, "HDCD decoding:  %s\n", ctx->settings.decode_hdcd ? "enabled" : "disabled");
194 
195     cyanrip_log(ctx, 0, "Album Art:      %s", ctx->nb_cover_arts == 0 ? "none" : "");
196     for (int i = 0; i < ctx->nb_cover_arts; i++) {
197         const char *title = dict_get(ctx->cover_arts[i].meta, "title");
198         const char *source = dict_get(ctx->cover_arts[i].meta, "source");
199         cyanrip_log(ctx, 0, "%s%s%s%s%s", title,
200                     source ? " (From: " : "",
201                     source ? source : "",
202                     source ? ")" : "",
203                     i != (ctx->nb_cover_arts - 1) ? ", " : "");
204     }
205     cyanrip_log(ctx, 0, "\n");
206 
207     cyanrip_log(ctx, 0, "Outputs:        ");
208     for (int i = 0; i < ctx->settings.outputs_num; i++)
209         cyanrip_log(ctx, 0, "%s%s", cyanrip_fmt_desc(ctx->settings.outputs[i]), i != (ctx->settings.outputs_num - 1) ? ", " : "");
210     cyanrip_log(ctx, 0, "\n");
211     CLOG("Disc number:    %s\n", ctx->meta, "disc");
212     CLOG("Total discs:    %s\n", ctx->meta, "totaldiscs");
213     cyanrip_log(ctx, 0, "Disc tracks:    %i\n", ctx->nb_cd_tracks);
214     cyanrip_log(ctx, 0, "Tracks to rip:  %s", (ctx->settings.rip_indices_count == -1) ? "all" : !ctx->settings.rip_indices_count ? "none" : "");
215     if (ctx->settings.rip_indices_count != -1) {
216         for (int i = 0; i < ctx->settings.rip_indices_count; i++)
217             cyanrip_log(ctx, 0, "%i%s", ctx->settings.rip_indices[i], i != (ctx->settings.rip_indices_count - 1) ? ", " : "");
218     }
219     cyanrip_log(ctx, 0, "\n");
220 
221     char duration[16];
222     cyanrip_frames_to_duration(ctx->duration_frames, duration);
223 
224     CLOG("DiscID:         %s\n", ctx->meta, "discid")
225     CLOG("Release ID:     %s\n", ctx->meta, "release_id")
226     CLOG("CDDB ID:        %s\n", ctx->meta, "cddb")
227     CLOG("Disc MCN:       %s\n", ctx->meta, "disc_mcn")
228     CLOG("Album:          %s\n", ctx->meta, "album")
229     CLOG("Album artist:   %s\n", ctx->meta, "album_artist")
230 
231     cyanrip_log(ctx, 0, "AccurateRip:    %s\n", ctx->ar_db_status == CYANRIP_ACCUDB_ERROR ? "error" :
232                                                 ctx->ar_db_status == CYANRIP_ACCUDB_NOT_FOUND ? "not found" :
233                                                 ctx->ar_db_status == CYANRIP_ACCUDB_FOUND ? "found" :
234                                                 ctx->ar_db_status == CYANRIP_ACCUDB_MISMATCH ? "mismatch" :
235                                                 "disabled");
236 
237     cyanrip_log(ctx, 0, "Total time:     %s\n", duration);
238 
239     cyanrip_log(ctx, 0, "\n");
240 }
241 
cyanrip_log_finish_report(cyanrip_ctx * ctx)242 void cyanrip_log_finish_report(cyanrip_ctx *ctx)
243 {
244     char t_s[64];
245     time_t t_c = time(NULL);
246     struct tm *t_l = localtime(&t_c);
247     strftime(t_s, sizeof(t_s), "%Y-%m-%dT%H:%M:%S", t_l);
248 
249     if (ctx->ar_db_status == CYANRIP_ACCUDB_FOUND) {
250         int accurip_verified = 0;
251         int accurip_partial = 0;
252         for (int i = 0; i < ctx->nb_tracks; i++) {
253             cyanrip_track *t = &ctx->tracks[i];
254             if (t->ar_db_status == CYANRIP_ACCUDB_FOUND) {
255                 if ((crip_find_ar(t, t->acurip_checksum_v1, 0) > 0) ||
256                     (crip_find_ar(t, t->acurip_checksum_v2, 0) > 0))
257                     accurip_verified++;
258                 else if (crip_find_ar(t, t->acurip_checksum_v1_450, 1) > (3*(t->ar_db_max_confidence+1)/4) &&
259                          t->acurip_checksum_v1_450)
260                     accurip_partial++;
261             }
262         }
263         cyanrip_log(ctx, 0, "Tracks ripped accurately: %i/%i\n", accurip_verified, ctx->nb_tracks);
264         if (accurip_partial)
265             cyanrip_log(ctx, 0, "Tracks ripped partially accurately: %i/%i\n",
266                         accurip_partial, ctx->nb_tracks - accurip_verified);
267         cyanrip_log(ctx, 0, "\n");
268     }
269 
270     cyanrip_log(ctx, 0, "Paranoia status counts:\n");
271 
272 #define PCHECK(PROP)                                                           \
273     if (paranoia_status[PARANOIA_CB_ ## PROP]) {                               \
274         const char *pstr = "    " #PROP ": ";                                  \
275         cyanrip_log(ctx, 0, "%s", pstr);                                       \
276         int padding = strlen("    FIXUP_DROPPED: ") - strlen(pstr);            \
277         for (int i = 0; i < padding; i++)                                      \
278             cyanrip_log(ctx, 0, " ");                                          \
279         cyanrip_log(ctx, 0, "%lu\n", paranoia_status[PARANOIA_CB_ ## PROP]);   \
280     }
281 
282     PCHECK(READ)
283     PCHECK(VERIFY)
284     PCHECK(FIXUP_EDGE)
285     PCHECK(FIXUP_ATOM)
286     PCHECK(SCRATCH)
287     PCHECK(REPAIR)
288     PCHECK(SKIP)
289     PCHECK(DRIFT)
290     PCHECK(BACKOFF)
291     PCHECK(OVERLAP)
292     PCHECK(FIXUP_DROPPED)
293     PCHECK(FIXUP_DUPED)
294     PCHECK(READERR)
295     PCHECK(CACHEERR)
296     PCHECK(WROTE)
297     PCHECK(FINISHED)
298     cyanrip_log(ctx, 0, "\n");
299 
300 #undef PCHECK
301 
302     cyanrip_log(ctx, 0, "Ripping errors: %i\n", ctx->total_error_count);
303     cyanrip_log(ctx, 0, "Ripping finished at %s\n", t_s);
304 }
305 
cyanrip_log_init(cyanrip_ctx * ctx)306 int cyanrip_log_init(cyanrip_ctx *ctx)
307 {
308     for (int i = 0; i < ctx->settings.outputs_num; i++) {
309         char *logfile = crip_get_path(ctx, CRIP_PATH_LOG, 1,
310                                       &crip_fmt_info[ctx->settings.outputs[i]],
311                                       NULL);
312 
313         ctx->logfile[i] = av_fopen_utf8(logfile, "w");
314 
315         if (!ctx->logfile) {
316             cyanrip_log(ctx, 0, "Error opening log file \"%s\" to write to!\n", logfile);
317             av_freep(&logfile);
318             return 1;
319         }
320 
321         av_freep(&logfile);
322     }
323 
324     return 0;
325 }
326 
cyanrip_log_end(cyanrip_ctx * ctx)327 void cyanrip_log_end(cyanrip_ctx *ctx)
328 {
329     for (int i = 0; i < ctx->settings.outputs_num; i++) {
330         if (!ctx->logfile[i])
331             continue;
332 
333         fclose(ctx->logfile[i]);
334         ctx->logfile[i] = NULL;
335     }
336 }
337 
338 static cyanrip_ctx *av_global_ctx = NULL;
339 static int av_log_indent = 0;
340 static int av_max_log_level = AV_LOG_QUIET;
341 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
342 
av_log_capture(void * ptr,int lvl,const char * format,va_list args)343 static void av_log_capture(void *ptr, int lvl, const char *format,
344                            va_list args)
345 {
346     pthread_mutex_lock(&log_lock);
347 
348     if (lvl > av_max_log_level)
349         goto end;
350 
351     if (av_global_ctx) {
352         for (int i = 0; i < av_global_ctx->settings.outputs_num; i++) {
353             if (!av_global_ctx->logfile[i])
354                 continue;
355 
356             for (int j = 0; j < av_log_indent; j++)
357                 fprintf(av_global_ctx->logfile[i], "    ");
358 
359             va_list args2;
360             va_copy(args2, args);
361             vfprintf(av_global_ctx->logfile[i], format, args2);
362             va_end(args2);
363         }
364     }
365 
366     for (int i = 0; i < av_log_indent; i++)
367         printf("    ");
368 
369     vprintf(format, args);
370 
371 end:
372     pthread_mutex_unlock(&log_lock);
373 }
374 
cyanrip_set_av_log_capture(cyanrip_ctx * ctx,int enable,int indent,int max_av_lvl)375 void cyanrip_set_av_log_capture(cyanrip_ctx *ctx, int enable,
376                                 int indent, int max_av_lvl)
377 {
378     pthread_mutex_lock(&log_lock);
379 
380     if (enable) {
381         av_global_ctx = ctx;
382         av_max_log_level = max_av_lvl;
383         av_log_indent = indent;
384         av_log_set_callback(av_log_capture);
385     } else {
386         av_log_set_callback(av_log_default_callback);
387         av_global_ctx = NULL;
388         av_log_indent = 0;
389         av_max_log_level = AV_LOG_QUIET;
390     }
391 
392     pthread_mutex_unlock(&log_lock);
393 }
394 
cyanrip_log(cyanrip_ctx * ctx,int verbose,const char * format,...)395 void cyanrip_log(cyanrip_ctx *ctx, int verbose, const char *format, ...)
396 {
397     pthread_mutex_lock(&log_lock);
398 
399     va_list args;
400     va_start(args, format);
401 
402     if (ctx) {
403         for (int i = 0; i < ctx->settings.outputs_num; i++) {
404             if (!ctx->logfile[i])
405                 continue;
406 
407             va_list args2;
408             va_copy(args2, args);
409             vfprintf(ctx->logfile[i], format, args2);
410             va_end(args2);
411         }
412     }
413 
414     vprintf(format, args);
415 
416     va_end(args);
417 
418     pthread_mutex_unlock(&log_lock);
419 }
420