bool OMX_MediaProcessorElement::openMedia(QString filepath) { if (!m_mediaProc || !m_texProvider) return false; if (!m_mediaProc->setFilename(filepath, m_textureId)) return false; emit textureReady(m_textureId); return true; }
void GLWidget::makeObject() { static const int coords[6][4][3] = { { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } }, { { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } }, { { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } }, { { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } }, { { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } }, { { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } } }; QElapsedTimer timer; timer.start(); #ifndef DISABLED_OPENMAX //loadWithOmx(); QPlatformNativeInterface* nativeInterface = QGuiApplicationPrivate::platformIntegration()->nativeInterface(); Q_ASSERT(nativeInterface); EGLDisplay eglDisplay = nativeInterface->nativeResourceForIntegration("egldisplay"); EGLContext eglContext = nativeInterface->nativeResourceForContext("eglcontext", QOpenGLContext::currentContext()); #if 0 eglImageVideo = getEGLImage(1920, 1080, eglDisplay, eglContext, textures[0]); #endif for (int i = 0; i < 5; i++) textures[i] = 0; //QtConcurrent::run(video_decode_test, videoPath, eglImageVideo, eglDisplay); m_videoProc = new OMX_VideoProcessor(eglDisplay, eglContext, m_provider); connect(m_videoProc, SIGNAL(textureReady(uint)), this, SLOT(onTextureChanged(uint))); m_videoProc->setVideoPath("/home/pi/out.h264"); m_videoProc->play(); #else for (int i = 0; i < 6; i++) { QPixmap pixmap(QString("%1%2.jpg").arg(prefix).arg(i)); if (pixmap.isNull()) LOG_ERROR(LOG_TAG, "Failed to load image!"); textures[i] = bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA); } #endif LOG_INFORMATION(LOG_TAG, "Elapsed: %lld.", timer.elapsed()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 4; ++j) { texCoords.append (QVector2D(j == 0 || j == 3, j == 0 || j == 1)); vertices.append (QVector3D(0.2 * coords[i][j][0], 0.2 * coords[i][j][1], 0.2 * coords[i][j][2])); } } }
QSGNode* OMX_MediaProcessorElement::updatePaintNode(QSGNode*, UpdatePaintNodeData*) { if (!m_texProvider) { m_texProvider = new OMX_TextureProviderQQuickItem(this); m_mediaProc = new OMX_MediaProcessor(m_texProvider); connect(m_mediaProc, SIGNAL(playbackCompleted()), this, SIGNAL(playbackCompleted())); connect(m_mediaProc, SIGNAL(playbackStarted()), this, SIGNAL(playbackStarted())); // Open if filepath is set. // TODO: Handle errors. if (!m_source.isNull()) { //if (QFile(m_source).exists()) { if (openMedia(m_source)) m_mediaProc->play(); //} //else { LOG_WARNING(LOG_TAG, "File does not exist."); //} } } return NULL; #if 0 QSGGeometryNode* node = 0; QSGGeometry* geometry = 0; if (!oldNode) { // Create the node. node = new QSGGeometryNode; geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); geometry->setDrawingMode(GL_TRIANGLE_STRIP); node->setGeometry(geometry); node->setFlag(QSGNode::OwnsGeometry); // TODO: Who is freeing this? // TODO: I cannot know the texture size here. QSGOpaqueTextureMaterial* material = new QSGOpaqueTextureMaterial; m_sgtexture = new OMX_SGTexture(m_texture, QSize(1920, 1080)); material->setTexture(m_sgtexture); node->setMaterial(material); node->setFlag(QSGNode::OwnsMaterial); #ifdef ENABLE_VIDEO_PROCESSOR QPlatformNativeInterface* nativeInterface = QGuiApplicationPrivate::platformIntegration()->nativeInterface(); Q_ASSERT(nativeInterface); EGLDisplay eglDisplay = nativeInterface->nativeResourceForIntegration("egldisplay"); EGLContext eglContext = nativeInterface->nativeResourceForContext( "eglcontext", QOpenGLContext::currentContext() ); #endif // Provider MUST be built in this thread. m_provider = new OMX_TextureProviderQQuickItem(this); #ifdef ENABLE_VIDEO_PROCESSOR m_videoProc = new OMX_VideoProcessor(eglDisplay, eglContext, m_provider); connect(m_videoProc, SIGNAL(textureReady(uint)), this, SLOT(onTextureChanged(uint))); if (!m_source.isNull()) m_videoProc->setVideoPath(m_source); if (m_playScheduled) { m_timer->start(30); m_videoProc->play(); } #elif ENABLE_MEDIA_PROCESSOR LOG_VERBOSE(LOG_TAG, "Starting video using media processor..."); m_mediaProc = new OMX_MediaProcessor(m_provider); m_mediaProc->setFilename("/home/pi/usb/Cars2.mkv", m_texture); //if (m_playScheduled) { m_timer->start(40); m_mediaProc->play(); //} #else LOG_VERBOSE(LOG_TAG, "Starting video..."); QtConcurrent::run(&startVideo, m_provider, this); m_timer->start(30); #endif } else { node = static_cast<QSGGeometryNode*>(oldNode); geometry = node->geometry(); geometry->allocate(4); // Update texture in the node if needed. QSGOpaqueTextureMaterial* material = (QSGOpaqueTextureMaterial*)node->material(); if (m_texture != (GLuint)material->texture()->textureId()) { // TODO: Does setTextureId frees the prev texture? // TODO: I should the given the texture size. LOG_ERROR(LOG_TAG, "Updating texture to %u!", m_texture); material = new QSGOpaqueTextureMaterial; m_sgtexture->setTexture(m_texture, QSize(1920, 1080)); } } // Create the vertices and map to texture. QRectF bounds = boundingRect(); QSGGeometry::TexturedPoint2D* vertices = geometry->vertexDataAsTexturedPoint2D(); vertices[0].set(bounds.x(), bounds.y() + bounds.height(), 0.0f, 0.0f); vertices[1].set(bounds.x() + bounds.width(), bounds.y() + bounds.height(), 1.0f, 0.0f); vertices[2].set(bounds.x(), bounds.y(), 0.0f, 1.0f); vertices[3].set(bounds.x() + bounds.width(), bounds.y(), 1.0f, 1.0f); return node; #endif }
/** * @brief OMX_MediaProcessor::setFilename Sets the filename and returns the texture * data that will be used. * @param filename * @param textureData * @return */ bool OMX_MediaProcessor::setFilename(QString filename, OMX_TextureData*& textureData) { QMutexLocker locker(&m_sendCmd); if (!checkCurrentThread()) return false; switch (m_state) { case STATE_INACTIVE: case STATE_STOPPED: break; case STATE_PAUSED: case STATE_PLAYING: return false; // TODO: Reimplement. } LOG_VERBOSE(LOG_TAG, "Opening video file..."); if (!m_omx_reader.Open(filename.toStdString(), true)) return false; m_filename = filename; m_bMpeg = m_omx_reader.IsMpegVideo(); m_has_video = m_omx_reader.VideoStreamCount(); m_has_audio = m_omx_reader.AudioStreamCount(); #ifdef ENABLE_SUBTITLES m_has_subtitle = m_omx_reader.SubtitleStreamCount(); #endif LOG_VERBOSE(LOG_TAG, "Initializing OMX clock..."); if (!m_av_clock->OMXInitialize(m_has_video, m_has_audio)) return false; if (ENABLE_HDMI_CLOCK_SYNC && !m_av_clock->HDMIClockSync()) return false; m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); m_omx_reader.GetHints(OMXSTREAM_VIDEO, m_hints_video); // Set audio stream to use. // TODO: Implement a way to change it runtime. #if 0 m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, m_audio_index_use); #endif // Seek on start? #if 0 if (m_seek_pos !=0 && m_omx_reader.CanSeek()) { printf("Seeking start of video to %i seconds\n", m_seek_pos); m_omx_reader.SeekTime(m_seek_pos * 1000.0f, 0, &startpts); // from seconds to DVD_TIME_BASE } #endif LOG_VERBOSE(LOG_TAG, "Opening video using OMX..."); if (m_has_video) if (!m_player_video->Open( m_hints_video, m_av_clock, textureData, false, /* deinterlace */ m_bMpeg, ENABLE_HDMI_CLOCK_SYNC, true, /* threaded */ 1.0 /* display aspect, unused */ )) return false; m_textureData = textureData; emit textureReady(textureData); #ifdef ENABLE_SUBTITLES LOG_VERBOSE(LOG_TAG, "Opening subtitles using OMX..."); if (m_has_subtitle) if (!m_player_subtitles->Open(FONT_PATH, FONT_SIZE, CENTERED, m_av_clock)) return false; // This is an upper bound check on the subtitle limits. When we pulled the subtitle // index from the user we check to make sure that the value is larger than zero, but // we couldn't know without scanning the file if it was too high. If this is the case // then we replace the subtitle index with the maximum value possible. if (m_has_subtitle && m_subtitle_index > (m_omx_reader.SubtitleStreamCount() - 1)) m_subtitle_index = m_omx_reader.SubtitleStreamCount() - 1; #endif m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); LOG_VERBOSE(LOG_TAG, "Opening audio using OMX..."); if (m_has_audio) if (!m_player_audio->Open( m_hints_audio, m_av_clock, &m_omx_reader, "omx:hdmi", /* TODO: implement way to change */ false, /* TODO: passthrough */ false, /* TODO: hw decode */ false, /* TODO: downmix boost */ true /* threaded */ )) return false; LOG_VERBOSE(LOG_TAG, "Executing clock..."); m_av_clock->SetSpeed(DVD_PLAYSPEED_NORMAL); /* TODO: Implement speed */ m_av_clock->OMXStateExecute(); m_state = STATE_STOPPED; return true; }
bool OMX_VideoGraph::playData(OMX_BUFFERHEADERTYPE*& buf) { if (port_settings_changed == 0 && compDecoder->waitForEvent(OMX_EventPortSettingsChanged, 131, 0, -1)) { LOG_VERBOSE(LOG_TAG, "Port settings changed event thrown!"); port_settings_changed = 1; tunnelDecSched->setupTunnel(0, TIMEOUT_MS); LOG_VERBOSE(LOG_TAG, "Setting scheduler to EXECUTING..."); compScheduler->sendCommand(OMX_CommandStateSet, OMX_StateExecuting, NULL); compScheduler->waitForEvent(OMX_EventCmdComplete, OMX_CommandStateSet, OMX_StateExecuting, TIMEOUT_MS); // video_render is in LOADED here. // now setup tunnel to video_render LOG_VERBOSE(LOG_TAG, "Setting up scheduler -> renderer tunnel..."); tunnelSchedRender->setupTunnel(0, TIMEOUT_MS); // video_render is in IDLE here. // Query output buffer requirements for renderer and provide the native display // the renderer will use. OMX_PARAM_PORTDEFINITIONTYPE portdef; portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); portdef.nVersion.nVersion = OMX_VERSION; portdef.nPortIndex = 221; compEGLRender->GetParameter(OMX_IndexParamPortDefinition, &portdef); portdef.format.video.pNativeWindow = m_eglDisplay; compEGLRender->SetParameter(OMX_IndexParamPortDefinition, &portdef); LOG_VERBOSE(LOG_TAG, "Waiting for EXECUTING state..."); compEGLRender->sendCommand(OMX_CommandStateSet, OMX_StateExecuting, NULL); compEGLRender->waitForEvent(OMX_EventCmdComplete, OMX_CommandStateSet, OMX_StateExecuting, TIMEOUT_MS); // Wait for port settings changed. LOG_VERBOSE(LOG_TAG, "Waiting for port settings changed!"); compEGLRender->waitForEvent(OMX_EventPortSettingsChanged, 221, 0, TIMEOUT_MS); // Enable output port of video_render. LOG_VERBOSE(LOG_TAG, "Enabling output port of EGL renderer..."); compEGLRender->sendCommand(OMX_CommandPortEnable, 221, NULL); // Instead of providing a buffer I provide the EGL image to use. GLuint texture; QMetaObject::invokeMethod( m_provider, "instantiateTexture", Qt::BlockingQueuedConnection, Q_RETURN_ARG(GLuint, texture), Q_ARG(QSize, QSize(1920, 1080))); m_texture.setTexture(eglImageVideo, texture, QSize(1920, 1080)); emit textureReady(texture); LOG_VERBOSE(LOG_TAG, "Providing EGLImage: %x.", (unsigned int)eglImageVideo); OMX_ERRORTYPE omxErr = OMX_UseEGLImage(compEGLRender->GetHandle(), &m_eglBuffer, 221, NULL, eglImageVideo); if (omxErr != OMX_ErrorNone) { LOG_ERROR(LOG_TAG, "OpenMAXILTextureLoader::decode - OMX_UseEGLImage - failed with omxErr(0x%x)\n", omxErr); return false; } compEGLRender->waitForEvent(OMX_EventCmdComplete, OMX_CommandPortEnable, 221, TIMEOUT_MS); LOG_VERBOSE(LOG_TAG, "Port enabled!!!"); // video_render is in EXECUTING here. } if (first_packet) { buf->nFlags = OMX_BUFFERFLAG_STARTTIME; first_packet = 0; } else buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; LOG_VERBOSE(LOG_TAG, "Calling empty this buffer (%x)...", (unsigned int)buf); compDecoder->EmptyThisBuffer(buf); if (m_eglBuffer && !fillCalled) { m_eglBuffer->nFilledLen = 0; LOG_VERBOSE(LOG_TAG, "Calling FillThisBuffer for the first time."); compEGLRender->FillThisBuffer(m_eglBuffer); fillCalled = true; } return true; }
//int main(int argc, char *argv[]) int startVideo(OMX_TextureProvider* provider, OMX_VideoSurfaceElement* element) { #ifdef ENABLE_ORIGINAL signal(SIGINT, sig_handler); if (isatty(STDIN_FILENO)) { struct termios new_termios; tcgetattr(STDIN_FILENO, &orig_termios); new_termios = orig_termios; new_termios.c_lflag &= ~(ICANON | ECHO | ECHOCTL | ECHONL); new_termios.c_cflag |= HUPCL; new_termios.c_cc[VMIN] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &new_termios); atexit(restore_termios); } else { orig_fl = fcntl(STDIN_FILENO, F_GETFL); fcntl(STDIN_FILENO, F_SETFL, orig_fl | O_NONBLOCK); atexit(restore_fl); } #endif LOG_VERBOSE(LOG_TAG, "Starting video..."); std::string last_sub = ""; std::string m_filename; double m_incr = 0; CRBP g_RBP; COMXCore g_OMX; bool m_stats = false; bool m_dump_format = false; bool m_3d = false; bool m_refresh = false; double startpts = 0; m_player_video = new OMXPlayerVideo(provider); m_player_audio = new OMXPlayerAudio; m_player_subtitles = new OMXPlayerSubtitles; QObject::connect(m_player_video, SIGNAL(textureReady(uint)), element, SLOT(onTextureChanged(uint))); TV_GET_STATE_RESP_T tv_state; const int boost_on_downmix_opt = 0x200; #ifdef ENABLE_ORIGINAL struct option longopts[] = { { "info", no_argument, NULL, 'i' }, { "help", no_argument, NULL, 'h' }, { "aidx", required_argument, NULL, 'n' }, { "adev", required_argument, NULL, 'o' }, { "stats", no_argument, NULL, 's' }, { "passthrough", no_argument, NULL, 'p' }, { "deinterlace", no_argument, NULL, 'd' }, { "hw", no_argument, NULL, 'w' }, { "3d", no_argument, NULL, '3' }, { "hdmiclocksync", no_argument, NULL, 'y' }, { "refresh", no_argument, NULL, 'r' }, { "sid", required_argument, NULL, 't' }, { "pos", required_argument, NULL, 'l' }, { "font", required_argument, NULL, 0x100 }, { "font-size", required_argument, NULL, 0x101 }, { "align", required_argument, NULL, 0x102 }, { "boost-on-downmix", no_argument, NULL, boost_on_downmix_opt }, { 0, 0, 0, 0 } }; int c; while ((c = getopt_long(argc, argv, "wihnl:o:cslpd3yt:r", longopts, NULL)) != -1) { switch (c) { case 'r': m_refresh = true; break; case 'y': m_hdmi_clock_sync = true; break; case '3': m_3d = true; break; case 'd': m_Deinterlace = true; break; case 'w': m_use_hw_audio = true; break; case 'p': m_passthrough = true; break; case 's': m_stats = true; break; case 'o': deviceString = optarg; if(deviceString != "local" && deviceString != "hdmi") { print_usage(); return 0; } deviceString = "omx:" + deviceString; break; case 'i': m_dump_format = true; break; case 't': m_subtitle_index = atoi(optarg) - 1; if(m_subtitle_index < 0) m_subtitle_index = 0; m_show_subtitle = true; break; case 'n': m_audio_index_use = atoi(optarg) - 1; if(m_audio_index_use < 0) m_audio_index_use = 0; break; case 'l': m_seek_pos = atoi(optarg) ; if (m_seek_pos < 0) m_seek_pos = 0; break; case 0x100: m_font_path = optarg; break; case 0x101: { const int thousands = atoi(optarg); if (thousands > 0) m_font_size = thousands*0.001f; } break; case 0x102: if (!strcmp(optarg, "center")) m_centered = true; else m_centered = false; break; case boost_on_downmix_opt: m_boost_on_downmix = true; break; case 0: break; case 'h': print_usage(); return 0; break; case ':': return 0; break; default: return 0; break; } } if (optind >= argc) { print_usage(); return 0; } #endif //m_filename = argv[optind]; m_filename = "/home/pi/usb/big_buck_bunny_1080p_h264.mov"; #ifdef ENABLE_ORIGINAL CLog::Init("./"); #endif g_RBP.Initialize(); g_OMX.Initialize(); m_av_clock = new OMXClock(); m_thread_player = true; LOG_VERBOSE(LOG_TAG, "Opening video file..."); if(!m_omx_reader.Open(m_filename.c_str(), m_dump_format)) #if 0 goto do_exit; #endif return 0; #ifdef ENABLE_ORIGINAL if(m_dump_format) goto do_exit; #endif m_bMpeg = m_omx_reader.IsMpegVideo(); m_has_video = m_omx_reader.VideoStreamCount(); m_has_audio = m_omx_reader.AudioStreamCount(); m_has_subtitle = m_omx_reader.SubtitleStreamCount(); LOG_VERBOSE(LOG_TAG, "Initializing OMX clock..."); if(!m_av_clock->OMXInitialize(m_has_video, m_has_audio)) #if 0 goto do_exit; #endif return 0; if(m_hdmi_clock_sync && !m_av_clock->HDMIClockSync()) #if 0 goto do_exit; #endif return 0; m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); m_omx_reader.GetHints(OMXSTREAM_VIDEO, m_hints_video); if(m_audio_index_use != -1) m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, m_audio_index_use); if(m_has_video && m_refresh) { memset(&tv_state, 0, sizeof(TV_GET_STATE_RESP_T)); m_BcmHost.vc_tv_get_state(&tv_state); if(m_filename.find("3DSBS") != string::npos) m_3d = true; SetVideoMode(m_hints_video.width, m_hints_video.height, m_hints_video.fpsrate, m_hints_video.fpsscale, m_3d); } // get display aspect TV_GET_STATE_RESP_T current_tv_state; memset(¤t_tv_state, 0, sizeof(TV_GET_STATE_RESP_T)); m_BcmHost.vc_tv_get_state(¤t_tv_state); if(current_tv_state.width && current_tv_state.height) m_display_aspect = (float)current_tv_state.width / (float)current_tv_state.height; // seek on start if (m_seek_pos !=0 && m_omx_reader.CanSeek()) { printf("Seeking start of video to %i seconds\n", m_seek_pos); m_omx_reader.SeekTime(m_seek_pos * 1000.0f, 0, &startpts); // from seconds to DVD_TIME_BASE } LOG_VERBOSE(LOG_TAG, "Opening video using OMX..."); uint textureId; if(m_has_video && !m_player_video->Open(m_hints_video, m_av_clock, textureId, m_Deinterlace, m_bMpeg, m_hdmi_clock_sync, m_thread_player, m_display_aspect)) #if 0 goto do_exit; #endif return 0; LOG_VERBOSE(LOG_TAG, "Opening subtitles using OMX..."); if(m_has_subtitle && !m_player_subtitles->Open(m_font_path, m_font_size, m_centered, m_av_clock)) #if 0 goto do_exit; #endif return 0; // This is an upper bound check on the subtitle limits. When we pulled the subtitle // index from the user we check to make sure that the value is larger than zero, but // we couldn't know without scanning the file if it was too high. If this is the case // then we replace the subtitle index with the maximum value possible. if(m_has_subtitle && m_subtitle_index > (m_omx_reader.SubtitleStreamCount() - 1)) { m_subtitle_index = m_omx_reader.SubtitleStreamCount() - 1; } // Here we actually enable the subtitle streams if we have one available. if (m_show_subtitle && m_has_subtitle && m_subtitle_index <= (m_omx_reader.SubtitleStreamCount() - 1)) m_omx_reader.SetActiveStream(OMXSTREAM_SUBTITLE, m_subtitle_index); m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); LOG_VERBOSE(LOG_TAG, "Opening audio using OMX..."); if(m_has_audio && !m_player_audio->Open(m_hints_audio, m_av_clock, &m_omx_reader, "omx:hdmi", m_passthrough, m_use_hw_audio, m_boost_on_downmix, m_thread_player)) #if 0 goto do_exit; #endif return 0; LOG_VERBOSE(LOG_TAG, "Executing clock..."); m_av_clock->SetSpeed(DVD_PLAYSPEED_NORMAL); m_av_clock->OMXStateExecute(); m_av_clock->OMXStart(); struct timespec starttime, endtime; printf("Subtitle count : %d state %s : index %d\n", m_omx_reader.SubtitleStreamCount(), m_show_subtitle ? "on" : "off", (m_omx_reader.SubtitleStreamCount() > 0) ? m_subtitle_index + 1 : m_subtitle_index); while(!m_stop) { int ch[8]; int chnum = 0; if(g_abort) goto do_exit; #if 0 while((ch[chnum] = getchar()) != EOF) chnum++; if (chnum > 1) ch[0] = ch[chnum - 1] | (ch[chnum - 2] << 8); switch(ch[0]) { case 'z': m_tv_show_info = !m_tv_show_info; vc_tv_show_info(m_tv_show_info); break; case '1': SetSpeed(m_av_clock->OMXPlaySpeed() - 1); break; case '2': SetSpeed(m_av_clock->OMXPlaySpeed() + 1); break; case 'j': if(m_has_audio) { int new_index = m_omx_reader.GetAudioIndex() - 1; if (new_index >= 0) m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, new_index); } break; case 'k': if(m_has_audio) m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, m_omx_reader.GetAudioIndex() + 1); break; case 'i': if(m_omx_reader.GetChapterCount() > 0) { m_omx_reader.SeekChapter(m_omx_reader.GetChapter() - 1, &startpts); FlushStreams(startpts); } else { m_incr = -600.0; } break; case 'o': if(m_omx_reader.GetChapterCount() > 0) { m_omx_reader.SeekChapter(m_omx_reader.GetChapter() + 1, &startpts); FlushStreams(startpts); } else { m_incr = 600.0; } break; case 'n': if(m_has_subtitle) { int new_index = m_subtitle_index-1; if(new_index >= 0) { m_subtitle_index = new_index; printf("Subtitle count : %d state %s : index %d\n", m_omx_reader.SubtitleStreamCount(), m_show_subtitle ? "on" : "off", (m_omx_reader.SubtitleStreamCount() > 0) ? m_subtitle_index + 1 : m_subtitle_index); m_omx_reader.SetActiveStream(OMXSTREAM_SUBTITLE, m_subtitle_index); m_player_subtitles->Flush(); } } break; case 'm': if(m_has_subtitle) { int new_index = m_subtitle_index+1; if(new_index < m_omx_reader.SubtitleStreamCount()) { m_subtitle_index = new_index; printf("Subtitle count : %d state %s : index %d\n", m_omx_reader.SubtitleStreamCount(), m_show_subtitle ? "on" : "off", (m_omx_reader.SubtitleStreamCount() > 0) ? m_subtitle_index + 1 : m_subtitle_index); m_omx_reader.SetActiveStream(OMXSTREAM_SUBTITLE, m_subtitle_index); m_player_subtitles->Flush(); } } break; case 's': if(m_has_subtitle) { if(m_show_subtitle) { m_omx_reader.SetActiveStream(OMXSTREAM_SUBTITLE, -1); m_player_subtitles->Flush(); m_show_subtitle = false; } else { m_omx_reader.SetActiveStream(OMXSTREAM_SUBTITLE, m_subtitle_index); m_show_subtitle = true; } printf("Subtitle count : %d state %s : index %d\n", m_omx_reader.SubtitleStreamCount(), m_show_subtitle ? "on" : "off", (m_omx_reader.SubtitleStreamCount() > 0) ? m_subtitle_index + 1 : m_subtitle_index); } break; case 'q': m_stop = true; goto do_exit; break; case 0x5b44: // key left if(m_omx_reader.CanSeek()) m_incr = -30.0; break; case 0x5b43: // key right if(m_omx_reader.CanSeek()) m_incr = 30.0; break; case 0x5b41: // key up if(m_omx_reader.CanSeek()) m_incr = 600.0; break; case 0x5b42: // key down if(m_omx_reader.CanSeek()) m_incr = -600.0; break; case ' ': case 'p': m_Pause = !m_Pause; if(m_Pause) { SetSpeed(OMX_PLAYSPEED_PAUSE); m_av_clock->OMXPause(); } else { SetSpeed(OMX_PLAYSPEED_NORMAL); m_av_clock->OMXResume(); } break; case '-': m_player_audio->SetCurrentVolume(m_player_audio->GetCurrentVolume() - 300); printf("Current Volume: %.2fdB\n", m_player_audio->GetCurrentVolume() / 100.0f); break; case '+': m_player_audio->SetCurrentVolume(m_player_audio->GetCurrentVolume() + 300); printf("Current Volume: %.2fdB\n", m_player_audio->GetCurrentVolume() / 100.0f); break; default: break; } #endif if(m_Pause) { OMXClock::OMXSleep(2); continue; } if(m_incr != 0 && !m_bMpeg) { int seek_flags = 0; double seek_pos = 0; double pts = 0; pts = m_av_clock->GetPTS(); seek_pos = (pts / DVD_TIME_BASE) + m_incr; seek_flags = m_incr < 0.0f ? AVSEEK_FLAG_BACKWARD : 0; seek_pos *= 1000.0f; m_incr = 0; if(m_omx_reader.SeekTime(seek_pos, seek_flags, &startpts)) FlushStreams(startpts); m_player_video->Close(); if(m_has_video && !m_player_video->Open(m_hints_video, m_av_clock, textureId, m_Deinterlace, m_bMpeg, m_hdmi_clock_sync, m_thread_player, m_display_aspect)) goto do_exit; } /* player got in an error state */ if(m_player_audio->Error()) { printf("audio player error. emergency exit!!!\n"); goto do_exit; } if(m_stats) { printf("V : %8.02f %8d %8d A : %8.02f %8.02f Cv : %8d Ca : %8d \r", m_av_clock->OMXMediaTime(), m_player_video->GetDecoderBufferSize(), m_player_video->GetDecoderFreeSpace(), m_player_audio->GetCurrentPTS() / DVD_TIME_BASE, m_player_audio->GetDelay(), m_player_video->GetCached(), m_player_audio->GetCached()); } if(m_omx_reader.IsEof() && !m_omx_pkt) { if (!m_player_audio->GetCached() && !m_player_video->GetCached()) break; // Abort audio buffering, now we're on our own if (m_buffer_empty) m_av_clock->OMXResume(); OMXClock::OMXSleep(10); continue; } /* when the audio buffer runs under 0.1 seconds we buffer up */ if(m_has_audio) { if(m_player_audio->GetDelay() < 0.1f && !m_buffer_empty) { if(!m_av_clock->OMXIsPaused()) { m_av_clock->OMXPause(); //printf("buffering start\n"); m_buffer_empty = true; clock_gettime(CLOCK_REALTIME, &starttime); } } if(m_player_audio->GetDelay() > (AUDIO_BUFFER_SECONDS * 0.75f) && m_buffer_empty) { if(m_av_clock->OMXIsPaused()) { m_av_clock->OMXResume(); //printf("buffering end\n"); m_buffer_empty = false; } } if(m_buffer_empty) { clock_gettime(CLOCK_REALTIME, &endtime); if((endtime.tv_sec - starttime.tv_sec) > 1) { m_buffer_empty = false; m_av_clock->OMXResume(); //printf("buffering timed out\n"); } } } if(!m_omx_pkt) m_omx_pkt = m_omx_reader.Read(); if(m_has_video && m_omx_pkt && m_omx_reader.IsActive(OMXSTREAM_VIDEO, m_omx_pkt->stream_index)) { if(m_player_video->AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); if(m_tv_show_info) { char response[80]; vc_gencmd(response, sizeof response, "render_bar 4 video_fifo %d %d %d %d", m_player_video->GetDecoderBufferSize()-m_player_video->GetDecoderFreeSpace(), 0 , 0, m_player_video->GetDecoderBufferSize()); vc_gencmd(response, sizeof response, "render_bar 5 audio_fifo %d %d %d %d", (int)(100.0*m_player_audio->GetDelay()), 0, 0, 100*AUDIO_BUFFER_SECONDS); } } else if(m_has_audio && m_omx_pkt && m_omx_pkt->codec_type == AVMEDIA_TYPE_AUDIO) { if(m_player_audio->AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else if(m_omx_pkt && m_omx_reader.IsActive(OMXSTREAM_SUBTITLE, m_omx_pkt->stream_index)) { if(m_omx_pkt->size && m_show_subtitle && (m_omx_pkt->hints.codec == CODEC_ID_TEXT || m_omx_pkt->hints.codec == CODEC_ID_SSA)) { if(m_player_subtitles->AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } } else { if(m_omx_pkt) { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } } } do_exit: printf("\n"); if(!m_stop && !g_abort) { if(m_has_audio) m_player_audio->WaitCompletion(); else if(m_has_video) m_player_video->WaitCompletion(); } if(m_refresh) { m_BcmHost.vc_tv_hdmi_power_on_best(tv_state.width, tv_state.height, tv_state.frame_rate, HDMI_NONINTERLACED, (EDID_MODE_MATCH_FLAG_T)(HDMI_MODE_MATCH_FRAMERATE|HDMI_MODE_MATCH_RESOLUTION|HDMI_MODE_MATCH_SCANMODE)); } m_av_clock->OMXStop(); m_av_clock->OMXStateIdle(); m_player_subtitles->Close(); m_player_video->Close(); m_player_audio->Close(); if(m_omx_pkt) { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } m_omx_reader.Close(); vc_tv_show_info(0); g_OMX.Deinitialize(); g_RBP.Deinitialize(); printf("have a nice day ;)\n"); return 1; }