7#define STB_IMAGE_IMPLEMENTATION
37 std::string result(str);
39 const std::string illegal =
"<>:\"/\\|?*";
41 std::ranges::replace_if(result, [&](
unsigned char c) {
return illegal.find(c) != std::string::npos || std::iscntrl(c); },
'_');
69 const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
71 AVPacket* packet = av_packet_alloc();
73 AVChannelLayout ch_layout = AV_CHANNEL_LAYOUT_STEREO;
75 SwrContext* swr_ctx =
nullptr;
77 swr_alloc_set_opts2(&swr_ctx,
87 if (swr_init(swr_ctx) < 0) {
103 cURLpp::Cleanup cleanup;
104 cURLpp::Easy easyhandle;
106 std::stringstream ss;
108 easyhandle.setOpt(cURLpp::Options::WriteStream(&ss));
109 easyhandle.perform();
111 std::string contentType;
112 cURLpp::infos::ContentType::get(easyhandle, contentType);
114 if (contentType ==
"image/jpeg") {
116 }
else if (contentType ==
"image/png") {
120 std::string s = ss.str();
129 unsigned char* stb_image = stbi_load_from_memory(
138 stbi_image_free(stb_image);
146 uint16_t* frame_input =
new uint16_t[rip_ctx->
c->frame_size*2];
149 for (
int i = 0; i < rip_ctx->
c->frame_size; i++) {
150 frame_input[(i*2)+0] =
consume();
151 frame_input[(i*2)+1] =
consume();
154 rip_ctx->
frame->nb_samples = i + 1;
159 int ret = swr_convert(rip_ctx->
swr_ctx,
160 rip_ctx->
frame->data,
161 rip_ctx->
frame->nb_samples,
162 reinterpret_cast<const uint8_t*
const *
>(&frame_input),
163 rip_ctx->
frame->nb_samples);
168 ret = avcodec_send_frame(rip_ctx->
c, rip_ctx->
frame);
174 ret = avcodec_receive_packet(rip_ctx->
c, rip_ctx->
packet);
175 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
177 }
else if (ret < 0) {
182 av_interleaved_write_frame(rip_ctx->
fmt_ctx, rip_ctx->
packet);
184 unsigned int progress = std::ceil(
_drive.progress() * 100);
193 ret = avcodec_send_frame(rip_ctx->
c,
nullptr);
199 ret = avcodec_receive_packet(rip_ctx->
c, rip_ctx->
packet);
200 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
202 }
else if (ret < 0) {
206 av_interleaved_write_frame(rip_ctx->
fmt_ctx, rip_ctx->
packet);
221 delete [] frame_input;
223 frame_input =
new uint16_t[rip_ctx->
c->frame_size*2];
230 delete [] frame_input;
235 av_packet_free(&rip_ctx->
packet);
239 AVCodecContext* c = avcodec_alloc_context3(rip_ctx->
codec);
241 c->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
242 c->sample_rate = 44100;
243 c->sample_fmt = AV_SAMPLE_FMT_FLTP;
244 c->bit_rate = 128000;
245 c->time_base = {1, 44100};
246 c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
248 avcodec_open2(c, rip_ctx->
codec,
nullptr);
252 AVFrame* frame = av_frame_alloc();
254 frame->nb_samples = c->frame_size;
255 frame->format = c->sample_fmt;
256 frame->ch_layout = c->ch_layout;
258 int ret = av_frame_get_buffer(frame, 0);
263 rip_ctx->
frame = frame;
265 std::stringstream ss;
266 ss << std::setw(2) << std::setfill(
'0') <<
_track;
277 avformat_alloc_output_context2(&rip_ctx->
fmt_ctx,
nullptr,
"ipod", output_filename.c_str());
281 rip_ctx->
st = avformat_new_stream(rip_ctx->
fmt_ctx, rip_ctx->
codec);
282 rip_ctx->
st->time_base = {1, 44100};
284 avcodec_parameters_from_context(rip_ctx->
st->codecpar, rip_ctx->
c);
286 rip_ctx->
sta = avformat_new_stream(rip_ctx->
fmt_ctx,
nullptr);
287 rip_ctx->
sta->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
291 rip_ctx->
sta->disposition |= AV_DISPOSITION_ATTACHED_PIC;
293 avio_open(&rip_ctx->
fmt_ctx->pb, output_filename.c_str(), AVIO_FLAG_WRITE);
294 ret = avformat_write_header(rip_ctx->
fmt_ctx,
nullptr);
304 av_write_trailer(rip_ctx->
fmt_ctx);
305 avio_closep(&rip_ctx->
fmt_ctx->pb);
306 avformat_free_context(rip_ctx->
fmt_ctx);
307 av_frame_free(&rip_ctx->
frame);
308 avcodec_free_context(&rip_ctx->
c);
376 std::stringstream ss;
379 av_dict_set(&rip_ctx->
fmt_ctx->metadata,
"title",
_disc.tracks()[
_track - 1].title().c_str(), 0);
380 av_dict_set(&rip_ctx->
fmt_ctx->metadata,
"artist",
_disc.artist().c_str(), 0);
381 av_dict_set(&rip_ctx->
fmt_ctx->metadata,
"album",
_disc.title().c_str(), 0);
382 av_dict_set(&rip_ctx->
fmt_ctx->metadata,
"date", ss.str().c_str(), 0);
383 av_dict_set(&rip_ctx->
fmt_ctx->metadata,
"genre",
_disc.genre().c_str(), 0);
390 AVPacket* pkt = av_packet_alloc();
397 pkt->stream_index = rip_ctx->
sta->index;
398 pkt->flags |= AV_PKT_FLAG_KEY;
399 av_interleaved_write_frame(rip_ctx->
fmt_ctx, pkt);
401 av_packet_free(&pkt);
Abstracts the host's disc drive.
Glib::Dispatcher _dispatcher
Used by _thread to safely emit _sig_track_progress.
int _album_art_image_size
The size of _album_art_image.
const DiscDB::Disc & _disc
The fully populated DiscDB::Disc describing the album.
std::string _output_filename
The output filename.
std::string _output_dir
The path to the album folder on the removable volume.
~CDRipper()
CDRipper destructor.
sig_done _sig_done
Emiited when the rip (whole disc or single track) is done.
std::string _album_art_url
The URL of the album art currently shown.
void rip()
Rip the entire disc.
Glib::Dispatcher _dispatcher_done
Used by _thread to safely emit _sig_done.
void end_rip(RipContext *rip_ctx)
Ends the ripping process.
CDRipper(CDDrive &drive, const DiscDB::Disc &disc, const std::string &albumArtURL, const std::string &mediaRoot)
CDRipper constructor.
std::string make_safe(const std::string &str) const
Used to sanitize folder and file names.
unsigned int _progress
The current percentage completion ripping the current track.
void add_file_art(RipContext *rip_ctx)
Adds _album_art_image to the output file.
void on_done_notification()
Called when _dispatcher_done is notified.
void on_notification()
Called when _dispatcher is notified.
CDDrive & _drive
The parent CDDrive.
void start_file(RipContext *rip_ctx)
Creates the ouput file, tags it and writes the M4A header.
sigc::signal< void(void)> sig_done
"Rip done" signal type.
unsigned int _track
The 1-based index of the track currently being ripped.
sigc::signal< void(unsigned int, unsigned int)> sig_track_progress
"Track progress made" signal type.
void ensure_output_dir()
Verifies _media_root exists and sets _output_dir.
AVCodecID _album_art_image_codec
Reflects whether the _album_art_image is JPEG or PNG.
uint8_t * _album_art_image
Raw bytes received from the _album_art_url.
void do_rip(RipContext *rip_ctx, bool continuous=true)
The core ripping method interacting directly with ffmpeg.
sig_done signal_done()
Getter for ::_signal_done.
sig_track_progress _sig_track_progress
Emitted when the percentage progress ripping a track has increased.
void rip_helper()
Private helper for rip().
std::thread * _thread
Used to execute the ripping process.
void end_file(RipContext *rip_ctx)
Frees resources in the ripping context specific to the output file.
void start_rip(RipContext *rip_ctx)
Begins the ripping process.
sig_track_progress signal_track_progress()
Getter for ::_signal_track_progress.
std::string _media_root
The path to the root of the removable volume.
std::pair< int, int > _album_art_image_dims
The dimensions of _album_art_image in pixels.
void tag_file(RipContext *rip_ctx)
Adds textual metadata (album and track information) to the output file.
virtual void producer(Producer< int16_t > *const producer)
Contains resources that are shared by private helper functions.
AVFrame * frame
The unit of input to ffmpeg.
AVStream * sta
Video stream for album art.
AVFormatContext * fmt_ctx
Format (file) context.
const AVCodec * codec
The M4A codec.
AVPacket * packet
The unit of output from ffmpeg.
AVCodecContext * c
The codec context.
AVStream * st
Audio stream.
SwrContext * swr_ctx
Context for resampling from int16_t to float.
Thrown when an error occurs mid-rip.