diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index a7a3a92d4..68e5bfc88 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -401,7 +401,7 @@ void PreferencesDialog::setup_ui() { QString locale_str = locale_file_basename.mid(locale_file_basename.lastIndexOf('_')+1); language_combobox->addItem(QLocale(locale_str).nativeLanguageName(), locale_relative_path); - if (config.language_file == locale_full_path) { + if (config.language_file == locale_relative_path) { language_combobox->setCurrentIndex(language_combobox->count() - 1); } } @@ -458,7 +458,7 @@ void PreferencesDialog::setup_ui() { general_layout->addWidget(new QLabel(tr("Thumbnail Resolution:"), this), row, 0, 1, 1); thumbnail_res_spinbox = new QSpinBox(this); - thumbnail_res_spinbox->setMinimum(1); + thumbnail_res_spinbox->setMinimum(0); thumbnail_res_spinbox->setMaximum(INT_MAX); thumbnail_res_spinbox->setValue(config.thumbnail_resolution); general_layout->addWidget(thumbnail_res_spinbox, row, 1, 1, 1); @@ -466,7 +466,7 @@ void PreferencesDialog::setup_ui() { general_layout->addWidget(new QLabel(tr("Waveform Resolution:"), this), row, 2, 1, 1); waveform_res_spinbox = new QSpinBox(this); - waveform_res_spinbox->setMinimum(1); + waveform_res_spinbox->setMinimum(0); waveform_res_spinbox->setMaximum(INT_MAX); waveform_res_spinbox->setValue(config.waveform_resolution); general_layout->addWidget(waveform_res_spinbox, row, 3, 1, 1); diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index 923a824a1..04d443b67 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -209,9 +209,17 @@ void PreviewGenerator::generate_waveform() { AVFrame* temp_frame = av_frame_alloc(); AVCodecContext** codec_ctx = new AVCodecContext* [fmt_ctx->nb_streams]; int64_t* media_lengths = new int64_t[fmt_ctx->nb_streams]{0}; + + // defaults to false, sets to true if we find a valid stream to make a preview of + bool create_previews = false; + for (unsigned int i=0;inb_streams;i++) { codec_ctx[i] = nullptr; - if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + + // we only generate previews for video and audio + // and only if the thumbnail and waveform sizes are > 0 + if ((fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && config.thumbnail_resolution > 0) + || (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && config.waveform_resolution > 0)) { AVCodec* codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id); if (codec != nullptr) { codec_ctx[i] = avcodec_alloc_context3(codec); @@ -220,189 +228,201 @@ void PreviewGenerator::generate_waveform() { if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_ctx[i]->channel_layout == 0) { codec_ctx[i]->channel_layout = av_get_default_channel_layout(fmt_ctx->streams[i]->codecpar->channels); } + create_previews = true; } } - } + } - // TODO may be unnecessary - doesn't av_read_frame allocate a packet itself? - AVPacket* packet = av_packet_alloc(); + if (create_previews) { + // TODO may be unnecessary - doesn't av_read_frame allocate a packet itself? + AVPacket* packet = av_packet_alloc(); - bool done = true; + bool done = true; - bool end_of_file = false; + bool end_of_file = false; - // get the ball rolling - do { - av_read_frame(fmt_ctx, packet); - } while (codec_ctx[packet->stream_index] == nullptr); - avcodec_send_packet(codec_ctx[packet->stream_index], packet); + // get the ball rolling + do { + av_read_frame(fmt_ctx, packet); + } while (codec_ctx[packet->stream_index] == nullptr); + avcodec_send_packet(codec_ctx[packet->stream_index], packet); - while (!end_of_file) { - while (codec_ctx[packet->stream_index] == nullptr || avcodec_receive_frame(codec_ctx[packet->stream_index], temp_frame) == AVERROR(EAGAIN)) { - av_packet_unref(packet); - int read_ret = av_read_frame(fmt_ctx, packet); + while (!end_of_file) { + while (codec_ctx[packet->stream_index] == nullptr || avcodec_receive_frame(codec_ctx[packet->stream_index], temp_frame) == AVERROR(EAGAIN)) { + av_packet_unref(packet); + int read_ret = av_read_frame(fmt_ctx, packet); - //dout << "read frame for" << footage->name << footage->url << read_ret << "retrieve_duration:" << retrieve_duration << "eof:" << end_of_file << "packet pts:" << packet->pts; + //dout << "read frame for" << footage->name << footage->url << read_ret << "retrieve_duration:" << retrieve_duration << "eof:" << end_of_file << "packet pts:" << packet->pts; - if (read_ret < 0) { - end_of_file = true; - if (read_ret != AVERROR_EOF) qCritical() << "Failed to read packet for preview generation" << read_ret; - break; - } - if (codec_ctx[packet->stream_index] != nullptr) { - int send_ret = avcodec_send_packet(codec_ctx[packet->stream_index], packet); - if (send_ret < 0 && send_ret != AVERROR(EAGAIN)) { - qCritical() << "Failed to send packet for preview generation - aborting" << send_ret; - end_of_file = true; - break; - } - } - } - if (!end_of_file) { - FootageStream* s = footage->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index); - if (s != nullptr) { - if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (!s->preview_done) { - int dstH = config.thumbnail_resolution; - int dstW = qRound(dstH * (float(temp_frame->width)/float(temp_frame->height))); - uint8_t* data = new uint8_t[size_t(dstW*dstH*4)]; + if (read_ret < 0) { + end_of_file = true; + if (read_ret != AVERROR_EOF) qCritical() << "Failed to read packet for preview generation" << read_ret; + break; + } + if (codec_ctx[packet->stream_index] != nullptr) { + int send_ret = avcodec_send_packet(codec_ctx[packet->stream_index], packet); + if (send_ret < 0 && send_ret != AVERROR(EAGAIN)) { + qCritical() << "Failed to send packet for preview generation - aborting" << send_ret; + end_of_file = true; + break; + } + } + } + if (!end_of_file) { + FootageStream* s = footage->get_stream_from_file_index(fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, packet->stream_index); + if (s != nullptr) { + if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!s->preview_done) { + int dstH = config.thumbnail_resolution; + int dstW = qRound(dstH * (float(temp_frame->width)/float(temp_frame->height))); + uint8_t* data = new uint8_t[size_t(dstW*dstH*4)]; - sws_ctx = sws_getContext( - temp_frame->width, - temp_frame->height, - static_cast(temp_frame->format), - dstW, - dstH, - static_cast(AV_PIX_FMT_RGBA), - SWS_FAST_BILINEAR, - nullptr, - nullptr, - nullptr - ); + sws_ctx = sws_getContext( + temp_frame->width, + temp_frame->height, + static_cast(temp_frame->format), + dstW, + dstH, + static_cast(AV_PIX_FMT_RGBA), + SWS_FAST_BILINEAR, + nullptr, + nullptr, + nullptr + ); - int linesize[AV_NUM_DATA_POINTERS]; - linesize[0] = dstW*4; - sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize); + int linesize[AV_NUM_DATA_POINTERS]; + linesize[0] = dstW*4; + sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize); - s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGBA8888, thumb_data_cleanup); - s->make_square_thumb(); + s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGBA8888, thumb_data_cleanup); + s->make_square_thumb(); - // is video interlaced? - s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE; - s->video_interlacing = s->video_auto_interlacing; + // is video interlaced? + s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE; + s->video_interlacing = s->video_auto_interlacing; - s->preview_done = true; + s->preview_done = true; - sws_freeContext(sws_ctx); + sws_freeContext(sws_ctx); - if (!retrieve_duration) { - avcodec_close(codec_ctx[packet->stream_index]); - codec_ctx[packet->stream_index] = nullptr; - } - } - media_lengths[packet->stream_index]++; - } else if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - int interval = qFloor((temp_frame->sample_rate/config.waveform_resolution)/4)*4; + if (!retrieve_duration) { + avcodec_close(codec_ctx[packet->stream_index]); + codec_ctx[packet->stream_index] = nullptr; + } + } + media_lengths[packet->stream_index]++; + } else if (fmt_ctx->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + int interval = qFloor((temp_frame->sample_rate/config.waveform_resolution)/4)*4; - AVFrame* swr_frame = av_frame_alloc(); - swr_frame->channel_layout = temp_frame->channel_layout; - swr_frame->sample_rate = temp_frame->sample_rate; - swr_frame->format = AV_SAMPLE_FMT_S16P; + AVFrame* swr_frame = av_frame_alloc(); + swr_frame->channel_layout = temp_frame->channel_layout; + swr_frame->sample_rate = temp_frame->sample_rate; + swr_frame->format = AV_SAMPLE_FMT_S16P; - swr_ctx = swr_alloc_set_opts( - nullptr, - temp_frame->channel_layout, - static_cast(swr_frame->format), - temp_frame->sample_rate, - temp_frame->channel_layout, - static_cast(temp_frame->format), - temp_frame->sample_rate, - 0, - nullptr - ); + swr_ctx = swr_alloc_set_opts( + nullptr, + temp_frame->channel_layout, + static_cast(swr_frame->format), + temp_frame->sample_rate, + temp_frame->channel_layout, + static_cast(temp_frame->format), + temp_frame->sample_rate, + 0, + nullptr + ); - swr_init(swr_ctx); + swr_init(swr_ctx); - swr_convert_frame(swr_ctx, swr_frame, temp_frame); + swr_convert_frame(swr_ctx, swr_frame, temp_frame); - // TODO implement a way to terminate this if the user suddenly closes the project while the waveform is being generated - int sample_size = av_get_bytes_per_sample(static_cast(swr_frame->format)); - int nb_bytes = swr_frame->nb_samples * sample_size; - int byte_interval = interval * sample_size; - for (int i=0;ichannels;j++) { - qint16 min = 0; - qint16 max = 0; - for (int k=0;kdata[j][i+k+1] << 8) | swr_frame->data[j][i+k]); - if (sample > max) { - max = sample; - } else if (sample < min) { - min = sample; - } - } else { - break; - } - } - s->audio_preview.append(min >> 8); - s->audio_preview.append(max >> 8); - if (cancelled) break; - } - } + // TODO implement a way to terminate this if the user suddenly closes the project while the waveform is being generated + int sample_size = av_get_bytes_per_sample(static_cast(swr_frame->format)); + int nb_bytes = swr_frame->nb_samples * sample_size; + int byte_interval = interval * sample_size; + for (int i=0;ichannels;j++) { + qint16 min = 0; + qint16 max = 0; + for (int k=0;kdata[j][i+k+1] << 8) | swr_frame->data[j][i+k]); + if (sample > max) { + max = sample; + } else if (sample < min) { + min = sample; + } + } else { + break; + } + } + s->audio_preview.append(min >> 8); + s->audio_preview.append(max >> 8); + if (cancelled) break; + } + } - swr_free(&swr_ctx); - av_frame_unref(swr_frame); - av_frame_free(&swr_frame); + swr_free(&swr_ctx); + av_frame_unref(swr_frame); + av_frame_free(&swr_frame); - if (cancelled) { - end_of_file = true; - break; - } - } - } + if (cancelled) { + end_of_file = true; + break; + } + } + } + + // check if we've got all our previews + if (retrieve_duration) { + done = false; + } else if (footage->audio_tracks.size() == 0) { + done = true; + for (int i=0;ivideo_tracks.size();i++) { + if (!footage->video_tracks.at(i).preview_done) { + done = false; + break; + } + } + if (done) { + end_of_file = true; + break; + } + } + av_packet_unref(packet); + } + } + + av_frame_free(&temp_frame); + av_packet_free(&packet); + + for (unsigned int i=0;inb_streams;i++) { + if (codec_ctx[i] != nullptr) { + avcodec_close(codec_ctx[i]); + avcodec_free_context(&codec_ctx[i]); + } + } + + // by this point, we'll have made all audio waveform previews + for (int i=0;iaudio_tracks.size();i++) { + footage->audio_tracks[i].preview_done = true; + } + } - // check if we've got all our previews - if (retrieve_duration) { - done = false; - } else if (footage->audio_tracks.size() == 0) { - done = true; - for (int i=0;ivideo_tracks.size();i++) { - if (!footage->video_tracks.at(i).preview_done) { - done = false; - break; - } - } - if (done) { - end_of_file = true; - break; - } - } - av_packet_unref(packet); - } - } - for (int i=0;iaudio_tracks.size();i++) { - footage->audio_tracks[i].preview_done = true; - } - av_frame_free(&temp_frame); - av_packet_free(&packet); - for (unsigned int i=0;inb_streams;i++) { - if (codec_ctx[i] != nullptr) { - avcodec_close(codec_ctx[i]); - avcodec_free_context(&codec_ctx[i]); - } - } if (retrieve_duration) { footage->length = 0; - int maximum_stream = 0; + unsigned int maximum_stream = 0; for (unsigned int i=0;inb_streams;i++) { if (media_lengths[i] > media_lengths[maximum_stream]) { maximum_stream = i; } } - footage->length = double(media_lengths[maximum_stream]) / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE; // TODO redo with PTS - finalize_media(); + + // FIXME: length is currently retrieved as a frame count rather than a timestamp + footage->length = qRound(double(media_lengths[maximum_stream]) / av_q2d(fmt_ctx->streams[maximum_stream]->avg_frame_rate) * AV_TIME_BASE); + + finalize_media(); } + delete [] media_lengths; delete [] codec_ctx; }