2 * Common Source Code Project for Qt : movie loader
3 * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
5 * History: June 30, 2016 : Initial. This refer from sample of ffmpeg .
9 #include "movie_loader.h"
10 #include "csp_logger.h"
13 #include <QMutexLocker>
15 MOVIE_LOADER::MOVIE_LOADER(OSD *osd, config_t *cfg) : QObject(NULL)
19 p_logger = osd->get_logger();
21 video_mutex = new QMutex(QMutex::Recursive);
22 snd_write_lock = new QMutex(QMutex::Recursive);
25 video_frame_count = 0;
27 audio_total_samples = 0;
37 hwaccel_method.clear();
46 old_dst_width = dst_width;
47 old_dst_height = dst_height;
52 #if defined(USE_LIBAV)
60 for(int i = 0; i < 4; i++) video_dst_data[i] = NULL;
61 for(int i = 0; i < 4; i++) video_dst_linesize[i] = 0;
62 video_dst_bufsize = 0;
63 video_stream_idx = -1;
64 audio_stream_idx = -1;
71 MOVIE_LOADER::~MOVIE_LOADER()
77 #if defined(USE_LIBAV)
78 int MOVIE_LOADER::decode_packet(int *got_frame, int cached)
81 int decoded = pkt.size;
85 if (pkt.stream_index == video_stream_idx) {
86 /* decode video frame */
87 ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
89 char str_buf[AV_ERROR_MAX_STRING_SIZE] = {0};
90 av_make_error_string(str_buf, AV_ERROR_MAX_STRING_SIZE, ret);
91 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error decoding video frame (%s)\n", str_buf);
95 if (frame->width != src_width || frame->height != src_height ||
96 frame->format != pix_fmt) {
97 /* To handle this change, one could call av_image_alloc again and
98 * decode the following frames into another rawvideo file. */
99 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error: Width, height and pixel format have to be "
100 "constant in a rawvideo file, but the width, height or "
101 "pixel format of the input video changed:\n"
102 "old: width = %d, height = %d, format = %s\n"
103 "new: width = %d, height = %d, format = %s\n",
104 src_width, src_height, av_get_pix_fmt_name(pix_fmt),
105 frame->width, frame->height,
106 av_get_pix_fmt_name((enum AVPixelFormat)frame->format));
109 if((old_dst_width != dst_width) || (old_dst_height != dst_height)) { // You sould allocate on opening.
110 QMutexLocker Locker_V(video_mutex);
111 //video_mutex->lock();
112 for(int i = 0; i < 4; i++) av_free(video_dst_data[i]);
113 ret = av_image_alloc(video_dst_data, video_dst_linesize,
114 dst_width, dst_height, AV_PIX_FMT_RGBA, 1);
117 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "MOVIE_LOADER: Could not re-allocate output buffer\n");
118 old_dst_width = dst_width;
119 old_dst_height = dst_height;
120 //video_mutex->unlock();
123 old_dst_width = dst_width;
124 old_dst_height = dst_height;
125 //video_mutex->unlock();
128 char str_buf2[AV_TS_MAX_STRING_SIZE] = {0};
132 //av_ts_make_time_string(str_buf2, frame->pts, &video_dec_ctx->time_base);
133 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_LOADER, "video_frame%s n:%d coded_n:%d pts:%s\n",
134 // cached ? "(cached)" : "",
135 // video_frame_count++, frame->coded_picture_number,
138 /* copy decoded frame to destination buffer:
139 * this is required since rawvideo expects non aligned data */
140 if(sws_context == NULL) {
141 sws_context = sws_getContext(frame->width, frame->height,
142 (enum AVPixelFormat) frame->format,
143 dst_width, dst_height,
145 SCALE_FLAGS, NULL, NULL, NULL);
146 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_LOADER, "Src frame=%dx%d Allocate frame: %dx%d", frame->width, frame->height, dst_width, dst_height);
148 if (sws_context == NULL) {
149 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER,
150 "MOVIE_LOADER: Could not initialize the conversion context\n");
156 sws_scale(sws_context,
157 frame->data, frame->linesize,
158 0, frame->height, video_dst_data, video_dst_linesize);
160 video_mutex->unlock();
162 } else if (pkt.stream_index == audio_stream_idx) {
163 /* decode audio frame */
164 ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
166 char str_buf[AV_ERROR_MAX_STRING_SIZE] = {0};
167 av_make_error_string(str_buf, AV_ERROR_MAX_STRING_SIZE, ret);
168 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error decoding audio frame (%s)\n", str_buf);
171 /* Some audio decoders decode only part of the packet, and have to be
172 * called again with the remainder of the packet data.
173 * Sample: fate-suite/lossless-audio/luckynight-partial.shn
174 * Also, some decoders might over-read the packet. */
175 decoded = FFMIN(ret, pkt.size);
178 //size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((enum AVSampleFormat)frame->format);
179 char str_buf[AV_TS_MAX_STRING_SIZE] = {0};
180 AVCodecContext *c = audio_stream->codec;
181 int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_context, c->sample_rate) + frame->nb_samples,
182 c->sample_rate, c->sample_rate, AV_ROUND_UP);
183 //av_ts_make_time_string(str_buf, frame->pts, &audio_dec_ctx->time_base);
184 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_LOADER,"audio_frame%s n:%d nb_samples:%d pts:%s\n",
185 // cached ? "(cached)" : "",
186 // audio_frame_count++, frame->nb_samples,
188 audio_total_samples += (int64_t)dst_nb_samples;
189 /* Write the raw audio data samples of the first plane. This works
190 * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
191 * most audio decoders output planar audio, which uses a separate
192 * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
193 * In other words, this code will write only the first audio channel
195 * You should use libswresample or libavfilter to convert the frame
197 sound_data_queue_t *px = (sound_data_queue_t *)malloc(sizeof(sound_data_queue_t));
199 px->data[0] = (uint8_t *)malloc((long)dst_nb_samples * 2 * sizeof(int16_t));
200 px->data[1] = px->data[2] = px->data[3] = NULL;
201 if(px->data[0] == NULL) {
203 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error while converting\n");
206 ret = swr_convert(swr_context,
207 px->data, dst_nb_samples,
208 (const uint8_t **)frame->data, frame->nb_samples);
212 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error while converting\n");
215 px->unpadded_linesize = (long)dst_nb_samples * 2 * sizeof(int16_t);
216 snd_write_lock->lock();
217 sound_data_queue.enqueue(px);
218 snd_write_lock->unlock();
220 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Error while converting\n");
227 /* If we use frame reference counting, we own the data and need
228 * to de-reference it when we don't use it anymore */
229 if (*got_frame && refcount)
230 av_frame_unref(frame);
235 int MOVIE_LOADER::open_codec_context(int *stream_idx,
236 AVFormatContext *fmt_ctx, enum AVMediaType type)
238 int ret, stream_index;
240 AVCodecContext *dec_ctx = NULL;
242 AVDictionary *opts = NULL;
244 ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
246 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not find %s stream in input file '%s'\n",
247 av_get_media_type_string(type), _filename.toLocal8Bit().constData());
251 st = fmt_ctx->streams[stream_index];
253 /* find decoder for the stream */
255 dec = avcodec_find_decoder(dec_ctx->codec_id);
257 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Failed to find %s codec\n",
258 av_get_media_type_string(type));
259 return AVERROR(EINVAL);
262 /* Init the decoders, with or without reference counting */
263 av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
264 if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
265 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Failed to open %s codec\n",
266 av_get_media_type_string(type));
269 *stream_idx = stream_index;
275 int MOVIE_LOADER::get_format_from_sample_fmt(const char **fmt,
276 enum AVSampleFormat sample_fmt)
279 struct sample_fmt_entry {
280 enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;
281 } sample_fmt_entries[] = {
282 { AV_SAMPLE_FMT_U8, "u8", "u8" },
283 { AV_SAMPLE_FMT_S16, "s16be", "s16le" },
284 { AV_SAMPLE_FMT_S32, "s32be", "s32le" },
285 { AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
286 { AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
290 for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
291 struct sample_fmt_entry *entry = &sample_fmt_entries[i];
292 if (sample_fmt == entry->sample_fmt) {
293 *fmt = AV_NE(entry->fmt_be, entry->fmt_le);
298 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER,
299 "sample format %s is not supported as output format\n",
300 av_get_sample_fmt_name(sample_fmt));
306 bool MOVIE_LOADER::open(QString filename)
308 int ret = 0, got_frame;
309 _filename = filename;
310 if(_filename.isEmpty()) return false;
316 video_frame_count = 0;
317 audio_total_samples = 0;
319 /* register all formats and codecs */
322 /* open input file, and allocate format context */
323 if (avformat_open_input(&fmt_ctx, _filename.toLocal8Bit().constData(), NULL, NULL) < 0) {
324 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not open source file %s\n", _filename.toLocal8Bit().constData());
328 /* retrieve stream information */
329 if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
330 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not find stream information\n");
334 if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
335 video_stream = fmt_ctx->streams[video_stream_idx];
336 video_dec_ctx = video_stream->codec;
338 /* allocate image where the decoded image will be put */
339 src_width = video_dec_ctx->width;
340 src_height = video_dec_ctx->height;
341 pix_fmt = video_dec_ctx->pix_fmt;
343 rate = av_stream_get_r_frame_rate(video_stream);
344 frame_rate = av_q2d(rate);
346 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not allocate raw video buffer\n");
349 video_dst_bufsize = ret;
352 if (open_codec_context(&audio_stream_idx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {
353 audio_stream = fmt_ctx->streams[audio_stream_idx];
354 audio_dec_ctx = audio_stream->codec;
355 sound_rate = audio_stream->codec->sample_rate;
357 swr_context = swr_alloc();
358 if(swr_context == NULL) {
359 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not allocate resampler context\n");
362 av_opt_set_int (swr_context, "in_channel_count", audio_stream->codec->channels, 0);
363 av_opt_set_int (swr_context, "in_sample_rate", audio_stream->codec->sample_rate, 0);
364 av_opt_set_sample_fmt(swr_context, "in_sample_fmt", audio_stream->codec->sample_fmt, 0);
365 av_opt_set_int (swr_context, "out_channel_count", 2, 0);
366 av_opt_set_int (swr_context, "out_sample_rate", sound_rate, 0);
367 av_opt_set_sample_fmt(swr_context, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
369 /* initialize the resampling context */
370 if ((ret = swr_init(swr_context)) < 0) {
371 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Failed to initialize the resampling context\n");
375 /* dump input information to stderr */
376 av_dump_format(fmt_ctx, 0, _filename.toLocal8Bit().constData(), 0);
377 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Video is %f fps", frame_rate);
378 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Audio is %d Hz ", sound_rate);
379 if (!audio_stream && !video_stream) {
380 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not find audio or video stream in the input, aborting\n");
385 frame = av_frame_alloc();
387 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "Could not allocate frame\n");
388 ret = AVERROR(ENOMEM);
392 /* initialize packet, set data to NULL, let the demuxer fill it */
393 av_init_packet(&pkt);
397 // Re-allocate buffer;
399 ret = av_image_alloc(video_dst_data, video_dst_linesize,
400 dst_width, dst_height, AV_PIX_FMT_RGBA, 1);
401 old_dst_width = dst_width;
402 old_dst_height = dst_height;
403 video_mutex->unlock();
405 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "MOVIE_LOADER: Could not re-allocate output buffer\n");
406 //video_mutex->unlock();
410 // ToDo : Initialize SWScaler and SWresampler.
411 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "MOVIE_LOADER: Loading movie completed.\n");
418 void MOVIE_LOADER::close(void)
420 if(video_dec_ctx != NULL) avcodec_close(video_dec_ctx);
421 if(audio_dec_ctx != NULL) avcodec_close(audio_dec_ctx);
422 avformat_close_input(&fmt_ctx);
423 if(sws_context != NULL) {
424 sws_freeContext(sws_context);
426 swr_free(&swr_context);
430 if(frame != NULL) av_frame_free(&frame);
431 for(int i = 0; i < 4; i++) {
432 if(video_dst_data[i] != NULL) av_free(video_dst_data[i]);
433 video_dst_data[i] = NULL;
434 video_dst_linesize[i] = 0;
436 video_mutex->unlock();
438 video_dec_ctx = NULL;
439 audio_dec_ctx = NULL;
442 video_frame_count = 0;
447 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_LOADER, "MOVIE_LOADER: Close movie.");
450 double MOVIE_LOADER::get_movie_frame_rate(void)
455 int MOVIE_LOADER::get_movie_sound_rate(void)
460 uint64_t MOVIE_LOADER::get_current_frame(void)
462 return video_frame_count;
465 bool MOVIE_LOADER::is_playing(void)
471 bool MOVIE_LOADER::is_pausing(void)
477 void MOVIE_LOADER::do_play()
483 void MOVIE_LOADER::do_stop()
488 // Still not closing.
491 void MOVIE_LOADER::do_pause(bool flag)
499 void MOVIE_LOADER::do_mute(bool left, bool right)
504 void MOVIE_LOADER::do_decode_frames(int frames, int width, int height)
507 bool end_of_frame = false;
509 double d_frames = (double)frames * (frame_rate / p_osd->vm_frame_rate());
510 if(now_pausing) return;
511 if(now_playing) duration_us = duration_us + (int64_t)((1.0e6 * (double)frames) / p_osd->vm_frame_rate());
513 mod_frames = mod_frames + d_frames;
514 real_frames = (int)mod_frames;
515 mod_frames = mod_frames - (double)real_frames;
517 if(width > 0) dst_width = width;
518 if(height > 0) dst_height = height;
520 if(real_frames <= 0) {
522 ///if(frames < 0) emit sig_decoding_error(MOVIE_LOADER_ILL_FRAME_NUMBER);
526 if(!now_playing || now_pausing) {
530 t = (double)(p_cfg->sound_frequency) / frame_rate;
531 l = 2 * sizeof(int16_t) * (int)(t + 0.5);
532 null_sound = (uint8_t *)malloc(l);
533 if(null_sound != NULL) {
534 memset(null_sound, 0x00, sizeof(null_sound));
535 emit sig_send_audio_frame(null_sound, l);
538 if(video_dst_data[0] != NULL) {
541 for(int yy = 0; yy < dst_height; yy++) {
542 q = (uint32_t *)(&(video_dst_data[0][yy * video_dst_linesize[0]]));
543 if((q != NULL) && (dst_width != 0)) {
544 memset(q, 0x00, dst_width * sizeof(uint32_t));
547 video_mutex->unlock();
555 v_f = (av_rescale_rnd(video_frame_count, 1000000000, (int64_t)(frame_rate * 1000.0), AV_ROUND_UP) < duration_us);
556 a_f = (av_rescale_rnd(audio_total_samples, 1000000, audio_stream->codec->sample_rate, AV_ROUND_UP) < duration_us);
557 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_LOADER, "%lld usec. V=%lld A=%lld, %d - %d\n", duration_us, video_frame_count, audio_total_samples, v_f, a_f);
558 if(!a_f && !v_f) break;
559 if(av_read_frame(fmt_ctx, &pkt) < 0) {
563 decode_packet(&got_frame, 0);
570 // if real_frames > 1 then clear screen ?
572 emit sig_movie_end(true);
579 void MOVIE_LOADER::get_video_frame()
581 uint32_t cacheline[8];
586 QMutexLocker Locker_S(p_osd->screen_mutex);
587 QMutexLocker Locker_V(video_mutex);
590 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_LOADER, "Transfer frame: %dx%d", dst_width, dst_height);
591 req_transfer = false;
592 for(int yy = 0; yy < dst_height; yy++) {
593 q = (uint32_t *)(p_osd->get_vm_screen_buffer(yy));
595 p = (uint32_t *)(&(video_dst_data[0][yy * video_dst_linesize[0]]));
596 if((p == NULL) || (q == NULL)) break;
597 for(xx = dst_width; xx > 7;) {
598 __DECL_VECTORIZED_LOOP
599 for(int i = 0; i < 8; i++) {
608 for(; xx > 0; xx--) {
612 //uint32_t col = 0xff000000 | (yy & 1) ? 0x00000000 : 0x00ff0000 | (yy & 2) ? 0x00000000 : 0x0000ff00 | (yy & 4) ? 0x00000000 : 0x000000ff;
613 uint32_t col = 0xffffffff;
615 for(xx = dst_width; xx > 7;) {
616 __DECL_VECTORIZED_LOOP
617 for(int i = 0; i < 8; i++) {
625 for(; xx > 0; xx--) {
633 void MOVIE_LOADER::do_seek_frame(bool relative, int frames)
638 void MOVIE_LOADER::do_dequeue_audio()
640 long audio_data_size = 0;
641 sound_data_queue_t *tmpq = NULL;
643 snd_write_lock->lock();
644 int il = sound_data_queue.count();
646 tmpq = sound_data_queue.at(il - 1);
648 audio_data_size += tmpq->unpadded_linesize;
653 uint8_t *tmpdata = NULL;
655 if(audio_data_size > 0) {
656 tmpdata = (uint8_t *)malloc(audio_data_size);
658 il = sound_data_queue.count();
659 for(ii = 0; ii < il; ii++) {
660 tmpq = sound_data_queue.dequeue();
662 if(tmpq->data[0] != NULL) {
663 if((tmpq->unpadded_linesize != 0) && (dptr < audio_data_size)) {
664 if(tmpdata != NULL) {
665 my_memcpy(&tmpdata[dptr], tmpq->data[0], tmpq->unpadded_linesize);
667 dptr += tmpq->unpadded_linesize;
675 snd_write_lock->unlock();
676 if((tmpdata != NULL) && (dptr != 0)) {
677 //emit sig_send_audio_frame(tmpdata,dptr); // Please free arg1 after calling sound_callback of vm.
678 //if(p_osd != NULL) p_osd->do_run_movie_audio_callback(tmpdata, dptr / (sizeof(int16_t) * 2));
679 if(p_osd != NULL) p_osd->do_run_movie_audio_callback(tmpdata, dptr);
683 void MOVIE_LOADER::do_set_enable_hwaccel_decoding(bool enable)
688 void MOVIE_LOADER::do_set_enable_hwaccel_scaling(bool enable)
693 void MOVIE_LOADER::do_set_dst_geometry(int width, int height)
695 if(width > 0) dst_width = width;
696 if(height > 0) dst_height = height;
699 void MOVIE_LOADER::do_set_dst_pixfmt(int type)