Платформа Android RTSP | RTMP инструкции по доступу к технологии проигрывателя прямых трансляций
Платформа Android RTSP | RTMP инструкции по доступу к технологии проигрывателя прямых трансляций

Технический бэкграунд

ДэниелЖивой SDKс2015выпущен в годуRTSP、RTMPМодуль прямой трансляции,Итерация никогда не останавливается,SmartPlayer обладает мощными функциями, высокой производительностью, высокой стабильностью, сверхнизкой задержкой и сверхнизким использованием ресурсов. Нет необходимости уточнять,Полностью самостоятельно разработанное ядро,Кроссплатформенная прямая трансляция RTSP и RTMP, единогласно признанная в отрасли. В этой статье в качестве примера рассматривается платформа Android.,Представьте, как Вниз интегрирует модули воспроизведения RTSP и RTMP.

Технология стыковки

Системные требования

  • SDK поддерживает Android5.1 и выше;
  • Поддерживаемые архитектуры ЦП: Armv7, Arm64, x86, x86_64.

Подготовка

  • Убедитесь, что файл SmartPlayerJniV2.java находится под именем пакета com.daniulive.smartplayer.(Доступно в других названиях пакетов Внизвызов);
  • В проект добавляется Smartavengine.jar;
  • Скопируйте SmartPlayerV2\app\src\main\jniLibs\armeabi-v7a, SmartPlayerV2\app\src\main\jniLibs\arm64-v8a, SmartPlayerV2\app\src\main\jniLibs\x86 и SmartPlayerV2\app\src\main\jniLibs \x86_64 Далее libSmartPlayer.so для проекта;
  • AndroidManifast.xml добавляет соответствующие разрешения:
Язык кода:java
копировать
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  • Связано с нагрузкой так:
Язык кода:java
копировать
static {  
    System.loadLibrary("SmartPlayer");
}
  • build.gradle настраивает 32/64-битные библиотеки:
Язык кода:java
копировать
splits {
    abi {
        enable true
        reset()
        // Specifies a list of ABIs that Gradle should create APKs for
        include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' //select ABIs to build APKs for
        // Specify that we do not want to also generate a universal APK that includes all ABIs
        universalApk true
    }
}
  • Если вам нужно интегрировать его в свою систему для тестирования, используйте Daniel Живой. SDK-приложение имя, авторизованная версия следует за авторизованным приложением Просто используйте имя как обычно;
  • Чтобы изменить имя приложения, внесите следующие изменения в strings.xml:
Язык кода:java
копировать
<string name="app_name">SmartPlayerSDKDemo</string>

Дизайн интерфейса

Подробное объяснение интерфейса SDK плеера Android RTSP | RTMP.

описание звонка

интерфейс

интерфейсописывать

Вызывается первым, в случае успеха будет возвращен экземпляр воспроизведения.

SmartPlayerOpen

Инициализировать проигрыватель, установить контекстную информацию, вернуть дескриптор проигрывателя

Обратный вызов события

SetSmartPlayerEventCallbackV2

Установить обратный вызов события

Настройки жесткого декодирования (H.264)

SetSmartPlayerVideoHWDecoder

Установите, использовать ли жесткое декодирование H.264 для воспроизведения. Если жесткое декодирование не поддерживается, оно автоматически адаптируется к мягкому декодированию.

Настройки жесткого декодирования (H.265)

SetSmartPlayerVideoHevcHWDecoder

Установите, использовать ли жесткое декодирование H.265 для воспроизведения. Если жесткое декодирование не поддерживается, оно автоматически адаптируется к мягкому декодированию.

видеоэкран Режим заполнения

SmartPlayerSetRenderScaleMode

Установите режим заполнения видеоэкрана, например, заполнение всего изображения или пропорциональное заполнение изображения. Если этот параметр не установлен, по умолчанию будет заполнено все изображение.

Установить тип рендеринга в режиме SurfaceView

SmartPlayerSetSurfaceRenderFormat

Установите тип рендеринга в режиме SurfaceView (когда второй параметр NTRenderer.CreateRenderer передает значение false) 0: формат RGB565, если не установлен, этот режим используется по умолчанию 1: формат ARGB8888;

Установите эффект сглаживания в режиме SurfaceView.

SmartPlayerSetSurfaceAntiAlias

Установите эффект сглаживания в режиме SurfaceView (если второй параметр NTRenderer.CreateRenderer имеет значение false. Примечание. После включения режима сглаживания производительность изображения может снизиться, используйте с осторожностью).

Установите поверхность воспроизведения

SmartPlayerSetSurface

Установите поверхность воспроизведения,если ноль,затем воспроизведи чистый звук

Установите режим самостоятельного рисования Mediacodec при жестком декодировании видео.

SmartPlayerSetHWRenderMode

В этом режиме совместимость и эффективность аппаратного декодирования лучше, а функции обратного вызова YUV/RGB, моментального снимка и масштабирования изображения будут недоступны.

Обновить поверхность жесткого декодирования

SmartPlayerUpdateHWRenderSurface

настраивать Обновить поверхность жесткого декодирования

Аудио обратный вызов

YUV/RGB

SmartPlayerSetExternalRender

Обеспечить декодированный интерфейс данных YUV/RGB,Для пользователей для рендеринга или дальнейшей обработки (например, анализа видео)

Audio

SmartPlayerSetExternalAudioOutput

Обратный вызов аудиоданных на верхний уровень (для вторичной обработки)

тип аудиовыхода

SmartPlayerSetAudioOutputType

Если для параметра use_audiotrack установлено значение 0, устройство вывода будет выбрано автоматически. Если для него установлено значение 1, будет использоваться режим аудиодорожки. В режиме эхоподавления «один к одному» выберите режим аудиодорожки.

Тип видеовыхода

NTRenderer.CreateRenderer (в верхней демо)

Второй параметр, если он равен true, использует openGLES для рисования, если false, используется SurfaceView по умолчанию.

режим воспроизведения

Настройка времени буфера

SmartPlayerSetBuffer

Установите буфер данных кэша завершения воспроизведения, единица измерения: миллисекунды. Если буфер не требуется, установите его на 0.

Открыть мгновенно на первом экране

SmartPlayerSetFastStartup

После настройки быстрого запуска,Если CDN кэширует GOP,выполнить Открыть мгновенно на первом экране

режим с низкой задержкой

SmartPlayerSetLowLatencyMode

Для ожиданий, аналогичных кукольным машинам в прямом эфире и т. д.сверхнизкая задержкасценарии использования,сверхнизкая задержкарежим воспроизведения Вниз,Задержка может достигать 200~400 мс.

Быстрое переключение URL-адресов

SmartPlayerSwitchPlaybackUrl

Быстрое переключение URL-адреса воспроизведения. При быстром переключении изменяется только часть источника воспроизведения. Он подходит для быстрого переключения между различными потоками данных (например, переключения между двумя камерами кукольной машины или переключения между потоками высокого и низкого разрешения).

Настройки режима RTSP TCP/UDP

SmartPlayerSetRTSPTcpMode

Установить режим RTSP TCP/UDP. Если этот параметр не установлен, будет использоваться режим UDP по умолчанию.

Настройка тайм-аута RTSP

SmartPlayerSetRTSPTimeout

Установите тайм-аут RTSP, единица измерения — секунды, должно быть больше 0.

Установите автоматическое переключение RTSP TCP/UDP

SmartPlayerSetRTSPAutoSwitchTcpUdp

Что касается RTSP, некоторые из них могут поддерживать rtp через udp, а некоторые могут поддерживать rtp через TCP. Для удобства использования в некоторых сценариях можно включить переключатель автоматической попытки. Если после включения udp невозможно воспроизвести, SDK автоматически попытается использовать TCP. Если tcp не может быть воспроизведен, SDK автоматически попытается использовать udp.

Установите имя пользователя и пароль RTSP

SetRTSPAuthenticationInfo

Если РТСП URL-адрес уже содержит имя пользователя и пароль, Имя пользователя и пароль, установленные для этого интерфейса, будут недействительны. То есть вам необходимо использовать имя пользователя и пароль, установленные этим интерфейсом, для аутентификации. RTSP URL-адрес не может содержать имя пользователя и пароль.

Отключение звука в реальном времени

SmartPlayerSetMute

Отключение звука в реальном времени

Установить громкость воспроизведения

SmartPlayerSetAudioVolume

Громкость окончания воспроизведения регулируется в реальном времени, диапазон: [0,100], 0 — звук без звука, 100 — максимальная громкость исходных потоковых данных.

Установите, отключать ли расширенный RTMP

DisableEnhancedRTMP

отключить расширенный RTMP, SDK по умолчанию включает расширенный RTMP

Скриншот в реальном времени

CaptureImage

Поддерживает форматы JPEG и PNG.

Видео поворот зеркала

вращать

SmartPlayerSetRotation

настраиватьпо часовой стрелкевращать, Обратите внимание, что помимо 0 градусов, Другие углы потребуют дополнительной производительности и в настоящее время поддерживаются. 0 градусов, 90 градусов, 180 градусов, 270 градусов вращать

горизонтальный разворот

SmartPlayerSetFlipHorizontal

настраиватьвидеогоризонтальный разворот

вертикальная инверсия

SmartPlayerSetFlipVertical

настраиватьвидеовертикальная инверсия

Установить URL

SmartPlayerSetUrl

Установите URL-адрес RTMP/RTSP, который необходимо воспроизвести или записать.

Начать играть

SmartPlayerStartPlay

Начать игратьRTSP/RTMPпоток

Хватит играть

SmartPlayerStopPlay

Хватит игратьRTSP/RTMPпоток

Закрыть экземпляр воспроизведения

SmartPlayerClose

В конце необходимо вызвать closeinterface для освобождения ресурсов.

Поддержка функций

  • Аудио: AAC/Speex(RTMP)/PCMA/PCMU;
  • Видео: H.264, H.265;
  • Протокол воспроизведения: RTSP|RTMP;
  • Поддержка чистого звука, чистого видео, воспроизведения аудио и видео;
  • Поддержка воспроизведения нескольких экземпляров;
  • Поддержка мягкого и жесткого декодирования для конкретных моделей;
  • Поддержка RTSP TCP, настроек режима UDP;
  • Поддерживает автоматическое переключение режимов RTSP TCP и UDP;
  • поддерживать Настройка тайм-аута RTSP,единица:Второй;
  • Поддержка настройки времени буфера, единица измерения: миллисекунды;
  • поддерживатьдогонятьрежим с низкой задержкой;
  • Поддерживает автоматическое переподключение при отключении, перехват видео и обратные вызовы, такие как состояние буфера;
  • Поддержка просмотра видео в режиме реального времени (0° 90° 180° 270°);
  • поддерживатьвидеоviewгоризонтальный разворот、вертикальная инверсия;
  • Поддержка рисования Surfaceview/OpenGL ES/TextureView;
  • Поддержка настройки режима заполнения видеоэкрана;
  • Аудио поддерживает режимы AudioTrack и OpenSL ES;
  • поддерживатьjpeg、pngСкриншот в реальном времени;
  • Поддержка регулировки громкости в режиме реального времени;
  • Поддержка обратного вызова аудио- и видеоданных перед декодированием;
  • Поддерживает обратный вызов данных YUV/RGB после декодирования;
  • Поддержка расширенного RTMP;
  • Поддержка расширенной функции записи видео;
  • Поддерживает Android 5.1 и выше.

Подробное объяснение вызова интерфейса

В этой статье используется Daniu Live SDK. Платформа Android SmartPlayerV2 является примером. Перед воспроизведением установите конфигурацию параметров инициализации (мягкое декодирование или жесткое декодирование, буфер). время и т. д.) и RTSP или RTMP, который необходимо воспроизвести. URL,точка Начать играть Вот и все。

При onCreate() сначала создается новый SmartPlayerJniV2():

Язык кода:java
копировать
/*
 * SmartPlayer.java
 * Author: daniusdk.com
 */
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_smart_player);
	
	...

    libPlayer = new SmartPlayerJniV2();
    myContext = this.getApplicationContext();
}

Начать играть、Хватит игратьвыполнить,Начать игратькогда,Вызовите InitAndSetConfig().,Полная инициализация общих параметров,Затем вызывайте только соответствующий другой интерфейс.

Язык кода:java
копировать
btnStartStopPlayback.setOnClickListener(new Button.OnClickListener() {

	// @Override
	public void onClick(View v) {

		if (isPlaying) {
			Log.i(TAG, "Stop playback stream++");

			int iRet = libPlayer.SmartPlayerStopPlay(playerHandle);

			if (iRet != 0) {
				Log.e(TAG, "Call SmartPlayerStopPlay failed..");
				return;
			}

			btnHardwareDecoder.setEnabled(true);
			btnLowLatency.setEnabled(true);

			if (!isRecording) {
				btnPopInputUrl.setEnabled(true);
				btnSetPlayBuffer.setEnabled(true);
				btnFastStartup.setEnabled(true);

				btnRecoderMgr.setEnabled(true);
				libPlayer.SmartPlayerClose(playerHandle);
				playerHandle = 0;
			}

			isPlaying = false;
			btnStartStopPlayback.setText("Начать играть ");

			if (is_enable_hardware_render_mode && sSurfaceView != null) {
				sSurfaceView.setVisibility(View.GONE);
				sSurfaceView.setVisibility(View.VISIBLE);
			}

			Log.i(TAG, "Stop playback stream--");
		} else {
			Log.i(TAG, "Start playback stream++");

			if (!isRecording) {
				InitAndSetConfig();
			}

			// Если для второго параметра установлено значение null, воспроизводится чистый звук.
			libPlayer.SmartPlayerSetSurface(playerHandle, sSurfaceView);

			libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);

			//int render_format = 1;
			//libPlayer.SmartPlayerSetSurfaceRenderFormat(playerHandle, render_format);

			//int is_enable_anti_alias = 1;
			//libPlayer.SmartPlayerSetSurfaceAntiAlias(playerHandle, is_enable_anti_alias);

			if (isHardwareDecoder && is_enable_hardware_render_mode) {
				libPlayer.SmartPlayerSetHWRenderMode(playerHandle, 1);
			}

			// External Render test
			//libPlayer.SmartPlayerSetExternalRender(playerHandle, new RGBAExternalRender(imageSavePath));
			//libPlayer.SmartPlayerSetExternalRender(playerHandle, new I420ExternalRender(imageSavePath));

			libPlayer.SmartPlayerSetUserDataCallback(playerHandle, new UserDataCallback());
			//libPlayer.SmartPlayerSetSEIDataCallback(playerHandle, new SEIDataCallback());

			libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 1);

			if (isMute) {
				libPlayer.SmartPlayerSetMute(playerHandle, isMute ? 1
						: 0);
			}

			if (isHardwareDecoder) {
				int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);

				int isSupportH264HwDecoder = libPlayer
						.SetSmartPlayerVideoHWDecoder(playerHandle, 1);

				Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
			}

			libPlayer.SmartPlayerSetLowLatencyMode(playerHandle, isLowLatency ? 1
					: 0);

			libPlayer.SmartPlayerSetFlipVertical(playerHandle, is_flip_vertical ? 1 : 0);

			libPlayer.SmartPlayerSetFlipHorizontal(playerHandle, is_flip_horizontal ? 1 : 0);

			libPlayer.SmartPlayerSetRotation(playerHandle, rotate_degrees);

			libPlayer.SmartPlayerSetAudioVolume(playerHandle, curAudioVolume);

			int iPlaybackRet = libPlayer
					.SmartPlayerStartPlay(playerHandle);

			if (iPlaybackRet != 0) {
				Log.e(TAG, "Call SmartPlayerStartPlay failed..");
				return;
			}

			btnStartStopPlayback.setText("Хватит играть ");

			btnPopInputUrl.setEnabled(false);
			btnPopInputKey.setEnabled(false);
			btnSetPlayBuffer.setEnabled(false);
			btnLowLatency.setEnabled(false);
			btnFastStartup.setEnabled(false);
			btnRecoderMgr.setEnabled(false);

			isPlaying = true;
			Log.i(TAG, "Start playback stream--");
		}
	}
});

Поскольку модули воспроизведения RTSP и RTMP, помимо обычной прямой трансляции, могут также записывать или извлекать потоки в реальном времени и пересылать их на RTMP-серверы или облегченные службы RTSP, поэтому базовая настройка параметров воспроизведения заканчивается записью и пересылкой. помещается в InitAndSetConfig() выполнить:

Язык кода:java
копировать
private void InitAndSetConfig() {
	playerHandle = libPlayer.SmartPlayerOpen(myContext);

	if (playerHandle == 0) {
		Log.e(TAG, "surfaceHandle with nil..");
		return;
	}

	libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,
			new EventHandeV2());

	libPlayer.SmartPlayerSetBuffer(playerHandle, playBuffer);

	// set report download скорость (по умолчанию — обратный вызов каждые 2 секунды) Пользователи могут самостоятельно настроить интервал отчетов.)
	libPlayer.SmartPlayerSetReportDownloadSpeed(playerHandle, 1, 2);

	libPlayer.SmartPlayerSetFastStartup(playerHandle, isFastStartup ? 1 : 0);

	//Установить Тайм-аут RTSP
	int rtsp_timeout = 10;
	libPlayer.SmartPlayerSetRTSPTimeout(playerHandle, rtsp_timeout);

	//Установить RTSP Автоматическое переключение режима TCP/UDP
	int is_auto_switch_tcp_udp = 1;
	libPlayer.SmartPlayerSetRTSPAutoSwitchTcpUdp(playerHandle, is_auto_switch_tcp_udp);

	libPlayer.SmartPlayerSaveImageFlag(playerHandle, 1);

	// It only used when playback RTSP stream..
	// libPlayer.SmartPlayerSetRTSPTcpMode(playerHandle, 1);

	// playbackUrl = "rtmp://localhost:1935/live/stream1";

	if (playbackUrl == null) {
		Log.e(TAG, "playback URL with NULL...");
		return;
	}

	libPlayer.SmartPlayerSetUrl(playerHandle, playbackUrl);
	// try_set_rtsp_url(playbackUrl);
}

Обработка обратного вызова события завершения воспроизведения EventHandle является очень важным средством обратной связи о состоянии. Помимо обратных вызовов состояния сети и буферизации, существуют также обратные вызовы для состояния записи, состояния моментального снимка и т. д.:

Язык кода:java
копировать
class EventHandeV2 implements NTSmartEventCallbackV2 {
	@Override
	public void onNTSmartEventCallbackV2(long handle, int id, long param1,
										 long param2, String param3, String param4, Object param5) {

		String player_event = "";

		switch (id) {
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
				player_event = "начинать..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
				player_event = «Соединяемся..»;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
				player_event = «Соединение не удалось.»;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
				player_event = «Соединение успешно..»;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
				player_event = «Соединение прервано.»;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
				player_event = "Хватит играть..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
				player_event = «Информация о разрешении: width: " + param1 + ", height: " + param2;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
				player_event = «Невозможно получить медиаданные, возможно, URL-адрес неправильный.»;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
				player_event = "Переключить URL воспроизведения..";
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
				player_event = "Снимок: " + param1 + " путь:" + param3;

				if (param1 == 0)
					player_event = player_event + ", Снимок сделан успешно";
				 else
					player_event = player_event + ", Не удалось сделать снимок";

				if (param4 != null && !param4.isEmpty())
					player_event += (", user data:" + param4);

				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
				player_event = «[запись] запускает новый видеофайл : " + param3;
				break;
			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
				player_event = «[запись] создала видеофайл : " + param3;
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
				Log.i(TAG, "Start Buffering");
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
				Log.i(TAG, "Buffering:" + param1 + "%");
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
				Log.i(TAG, "Stop Buffering");
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
				player_event = "download_speed:" + param1 + "Byte/s" + ", "
						+ (param1 * 8 / 1000) + "kbps" + ", " + (param1 / 1024)
						+ "KB/s";
				break;

			case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RTSP_STATUS_CODE:
				Log.e(TAG, "RTSP error code received, please make sure username/password is correct, error code:" + param1);
				player_event = "RTSP error code:" + param1;
				break;
		}

		if (player_event.length() > 0) {
			Log.i(TAG, player_event);
			Message message = new Message();
			message.what = PLAYER_EVENT_MSG;
			message.obj = player_event;
			handler.sendMessage(message);
		}
	}
}

Если РТСП、RTMPпотокнуждаться Видео:

Язык кода:java
копировать
btnStartStopRecorder.setOnClickListener(new Button.OnClickListener() {

	// @Override
	public void onClick(View v) {

		if (isRecording) {

			int iRet = libPlayer.SmartPlayerStopRecorder(playerHandle);

			if (iRet != 0) {
				Log.e(TAG, "Call SmartPlayerStopRecorder failed..");
				return;
			}

			if (!isPlaying) {
				btnPopInputUrl.setEnabled(true);
				btnSetPlayBuffer.setEnabled(true);
				btnFastStartup.setEnabled(true);
				btnRecoderMgr.setEnabled(true);

				libPlayer.SmartPlayerClose(playerHandle);
				playerHandle = 0;
			}

			btnStartStopRecorder.setText(" Начать запись");

			isRecording = false;
		} else {
			Log.i(TAG, "onClick start recorder..");

			if (!isPlaying) {
				InitAndSetConfig();
			}

			ConfigRecorderFunction();

			int startRet = libPlayer.SmartPlayerStartRecorder(playerHandle);

			if (startRet != 0) {
				Log.e(TAG, "Failed to start recorder.");
				return;
			}

			btnPopInputUrl.setEnabled(false);
			btnSetPlayBuffer.setEnabled(false);
			btnFastStartup.setEnabled(false);
			btnRecoderMgr.setEnabled(false);

			isRecording = true;
			btnStartStopRecorder.setText("Остановить запись");
		}
	}
});

в,Настройки параметров конфигурации записи, такие как Вниз,В дополнение к демонстрации интерфейса Вниз face,Вы также можете настроить запись только видео или аудио:

Язык кода:java
копировать
void ConfigRecorderFunction() {
	if (libPlayer != null) {
		int is_rec_trans_code = 1;
		libPlayer.SmartPlayerSetRecorderAudioTranscodeAAC(playerHandle, is_rec_trans_code);

		if (recDir != null && !recDir.isEmpty()) {
			int ret = libPlayer.SmartPlayerCreateFileDirectory(recDir);
			if (0 == ret) {
				if (0 != libPlayer.SmartPlayerSetRecorderDirectory(
						playerHandle, recDir)) {
					Log.e(TAG, "Set recoder dir failed , path:" + recDir);
					return;
				}

				if (0 != libPlayer.SmartPlayerSetRecorderFileMaxSize(
						playerHandle, 200)) {
					Log.e(TAG,
							"SmartPublisherSetRecorderFileMaxSize failed.");
					return;
				}

			} else {
				Log.e(TAG, "Create recorder dir failed, path:" + recDir);
			}
		}
	}
}

Чтобы играть во время Скриншота в первое время:

Язык кода:java
копировать
btnCaptureImage.setOnClickListener(new Button.OnClickListener() {
	@SuppressLint("SimpleDateFormat")
	public void onClick(View v) {
		if (0 == playerHandle)
			return;

		if (null == capture_image_date_format_)
			capture_image_date_format_ = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS");

		String timestamp = capture_image_date_format_.format(new Date());
		String imageFileName = timestamp;

		String image_path = imageSavePath + "/" + imageFileName;

		int quality;
		boolean is_jpeg = true;
		if (is_jpeg) {
			image_path += ".jpeg";
			quality = 100;
		}
		else {
			image_path += ".png";
			quality = 100;
		}

		int capture_ret = libPlayer.CaptureImage(playerHandle,is_jpeg?0:1, quality, image_path, "test cix");
		Log.i(TAG, "capture image ret:" + capture_ret + ", file:" + image_path);
	}
});

Если вам нужно перевернуть видео по горизонтали, вертикали или повернуть:

Язык кода:java
копировать
btnFlipVertical.setOnClickListener(new Button.OnClickListener() {
	public void onClick(View v) {
		is_flip_vertical = !is_flip_vertical;

		if (is_flip_vertical) {
			btnFlipVertical.setText("Отменить переворот");
		} else {
			btnFlipVertical.setText("вертикальная инверсия");
		}

		if (playerHandle != 0) {
			libPlayer.SmartPlayerSetFlipVertical(playerHandle,
					is_flip_vertical ? 1 : 0);
		}
	}
});

btnFlipHorizontal.setOnClickListener(new Button.OnClickListener() {
	public void onClick(View v) {
		is_flip_horizontal = !is_flip_horizontal;

		if (is_flip_horizontal) {
			btnFlipHorizontal.setText("Отменить переворот");
		} else {
			btnFlipHorizontal.setText("горизонтальный разворот");
		}

		if (playerHandle != 0) {
			libPlayer.SmartPlayerSetFlipHorizontal(playerHandle,is_flip_horizontal ? 1:0);
		}
	}
});

btnRotation.setOnClickListener(new Button.OnClickListener() {
	общественная недействительность onClick (Просмотр v) {

		вращать_градусы += 90;
		вращать_градусы = вращать_градусы% 360;

		если (0 == поворот_градусов) {
			btnRotation.setText("вращать90степень");
		} else if (90 == Rotate_grades) {
			btnRotation.setText("вращать180степень");
		} else if (180 == Rotate_grades) {
			btnRotation.setText("вращать270 градусов");
		} else if (270 == Rotate_grades) {
			btnRotation.setText("Нетвращать");
		}

		если (playerHandle != 0) {
			libPlayer.SmartPlayerSetRotation(playerHandle,
					rotate_degrees);
		}
	}
});

При onDestroy() остановите воспроизведение, запись и отпустите дескриптор экземпляра проигрывателя:

Язык кода:java
копировать
@Override
protected void onDestroy() {
	Log.i(TAG, "Run into activity destory++");

	if (playerHandle != 0) {
		if (isPlaying) {
			libPlayer.SmartPlayerStopPlay(playerHandle);
		}

		if (isRecording) {
			libPlayer.SmartPlayerStopRecorder(playerHandle);
		}

		libPlayer.SmartPlayerClose(playerHandle);
		playerHandle = 0;
	}
	super.onDestroy();
	finish();
	System.exit(0);
}

Вышеупомянутый процесс является грубым. Если вам нужно воспроизвести несколько экземпляров, вы можете сделать простую инкапсуляцию. Эффект от нескольких экземпляров следующий:

Эталонный код инкапсуляции LibPlayerWrapper.java выглядит следующим образом. Если вам нужны дополнительные функции, просто добавьте их в соответствии со структурой дизайна:

Язык кода:java
копировать
/*
 * LibPlayerWrapper.java.java
 * Author: daniusdk.com
 */
package com.daniulive.smartplayer;

import android.content.Context;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;

import com.eventhandle.NTSmartEventCallbackV2;
import java.lang.ref.WeakReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LibPlayerWrapper {
    private static String TAG = "NTLogLibPlayerW";
    private static final int OK = 0;

    private WeakReference<Context> context_;
    private final ReadWriteLock rw_lock_ = new ReentrantReadWriteLock(true);
    private final java.util.concurrent.locks.Lock write_lock_ = rw_lock_.writeLock();
    private final java.util.concurrent.locks.Lock read_lock_ = rw_lock_.readLock();

    private SmartPlayerJniV2 lib_player_;
    private volatile long native_handle_;
    private View view_;

    private volatile boolean is_playing_;
    private volatile boolean is_recording_;

    private WeakReference<EventListener> event_listener_;

    public LibPlayerWrapper(SmartPlayerJniV2 lib_player, Context context, EventListener listener) {
        if (!empty())
            throw new IllegalStateException("it is not empty");

        if (null == lib_player)
            throw new NullPointerException("lib_player is null");

        this.lib_player_ = lib_player;

        if (context != null)
            this.context_ = new WeakReference<>(context);

        if (listener == null ) {
            this.event_listener_ = null;
        }
        else {
            this.event_listener_ = new WeakReference<>(listener);
        }
    }

    private void clear_all_playing_flags() {
        this.is_playing_ = false;
        this.is_recording_ = false;
    }

    public void set(long handle) {
        if (!empty())
            throw new IllegalStateException("it is not empty");

        write_lock_.lock();
        try {
            clear_all_playing_flags();
            this.native_handle_ = handle;
        } finally {
            write_lock_.unlock();
        }

        Log.i(TAG, "set native_handle:" + handle);
    }

    public void SetView(View view) {
        Log.i(TAG, "SetView: " + view);
        this.view_ = view;
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (check_native_handle()) {
                if(is_playing()) {
                    lib_player_.SmartPlayerStopPlay(get());
                    this.is_playing_ = false;
                }

                if(is_recording()) {
                    lib_player_.SmartPlayerStopRecorder(get());
                    this.is_recording_ = false;
                }

                lib_player_.SmartPlayerClose(this.native_handle_);
                Log.i(TAG, "finalize close handle:" + this.native_handle_);
                this.native_handle_ = 0;
            }
        }catch (Exception e) {

        }

        super.finalize();
    }

    public void release() {
        if (empty())
            return;

        if(is_playing())
            StopPlayer();

        if (is_recording())
            StopRecorder();

        long handle;
        write_lock_.lock();
        try {
            handle = this.native_handle_;
            this.native_handle_ = 0;
            clear_all_playing_flags();
        } finally {
            write_lock_.unlock();
        }

        if (lib_player_ != null && handle != 0)
            lib_player_.SmartPlayerClose(handle);
    }

    public boolean try_release() {
        if (empty())
            return false;

        if (is_player_running()) {
            Log.i(TAG, "try_release it is running, native_handle:" + get());
            return false;
        }

        long handle;
        write_lock_.lock();
        try {
            if (is_player_running())
                return false;

            handle = this.native_handle_;
            this.native_handle_ = 0;
        } finally {
            write_lock_.unlock();
        }

        if (lib_player_ != null && handle != 0)
            lib_player_.SmartPlayerClose(handle);

        return true;
    }

    public final boolean empty() { return 0 == this.native_handle_; }

    public final long get() { return this.native_handle_; }

    public View get_view() {return this.view_;}

    public final boolean check_native_handle() {
        return this.lib_player_ != null && this.native_handle_ != 0;
    }

    public final boolean is_playing() { return is_playing_; }

    public final boolean is_recording() { return is_recording_; }

    public final boolean is_player_running() { return is_playing_ || is_recording_; }

    private boolean isValidRtspOrRtmpUrl(String url) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        return url.trim().startsWith("rtsp://") || url.startsWith("rtmp://");
    }

    private EventListener getListener() {
        if ( this.event_listener_ == null )
            return null;

        return this.event_listener_.get();
    }

    protected final Context application_context() {
        if (null == context_)
            return null;

        return context_.get();
    }

    public boolean OpenPlayerHandle(String playback_url, int play_buffer, int is_using_tcp) {

        if (check_native_handle())
            return true;

        if(!isValidRtspOrRtmpUrl(playback_url))
            return false;

        long handle = lib_player_.SmartPlayerOpen(application_context());
        if (0==handle) {
            Log.e(TAG, "sdk open failed!");
            return false;
        }

        lib_player_.SetSmartPlayerEventCallbackV2(handle, new EventHandleV2());

        lib_player_.SmartPlayerSetBuffer(handle, play_buffer);

        // set report download скорость (по умолчанию — обратный вызов каждые 2 секунды) Пользователи могут самостоятельно настроить интервал отчетов.)
        lib_player_.SmartPlayerSetReportDownloadSpeed(handle, 1, 4);

        boolean isFastStartup = true;
        lib_player_.SmartPlayerSetFastStartup(handle, isFastStartup ? 1 : 0);

        //Установить Тайм-аут RTSP
        int rtsp_timeout = 10;
        lib_player_.SmartPlayerSetRTSPTimeout(handle, rtsp_timeout);

        //Установить RTSP Автоматическое переключение режима TCP/UDP
        int is_auto_switch_tcp_udp = 1;
        lib_player_.SmartPlayerSetRTSPAutoSwitchTcpUdp(handle, is_auto_switch_tcp_udp);

        lib_player_.SmartPlayerSaveImageFlag(handle, 1);

        // It only used when playback RTSP stream..
        lib_player_.SmartPlayerSetRTSPTcpMode(handle, is_using_tcp);

        lib_player_.DisableEnhancedRTMP(handle, 0);

        lib_player_.SmartPlayerSetUrl(handle, playback_url);

        set(handle);

        return true;
    }

    private void SetPlayerParam(boolean is_hardware_decoder, boolean is_enable_hardware_render_mode, boolean is_mute)
    {
         Surface surface = null;
         int surface_codec_media_color_format = 0;

         if (view_ != null && view_ instanceof SurfaceView && ((SurfaceView) view_).getHolder() != null)
             surface = ((SurfaceView) view_).getHolder().getSurface();

         lib_player_.SetSurface(get(), surface, surface_codec_media_color_format, 0, 0);

        lib_player_.SmartPlayerSetRenderScaleMode(get(), 1);

        //int render_format = 1;
        //lib_player.SmartPlayerSetSurfaceRenderFormat(handle, render_format);

        //int is_enable_anti_alias = 1;
        //lib_player.SmartPlayerSetSurfaceAntiAlias(handle, is_enable_anti_alias);

        if (is_hardware_decoder && is_enable_hardware_render_mode) {
            lib_player_.SmartPlayerSetHWRenderMode(get(), 1);
        }

        lib_player_.SmartPlayerSetAudioOutputType(get(), 1);

        lib_player_.SmartPlayerSetMute(get(), is_mute ? 1 : 0);

        if (is_hardware_decoder) {
            int isSupportHevcHwDecoder = lib_player_.SetSmartPlayerVideoHevcHWDecoder(get(), 1);

            int isSupportH264HwDecoder = lib_player_.SetSmartPlayerVideoHWDecoder(get(), 1);

            Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
        }

        boolean isLowLatency = true;
        lib_player_.SmartPlayerSetLowLatencyMode(get(), isLowLatency ? 1 : 0);

        boolean is_flip_vertical = false;
        lib_player_.SmartPlayerSetFlipVertical(get(), is_flip_vertical ? 1 : 0);

        boolean is_flip_horizontal = false;
        lib_player_.SmartPlayerSetFlipHorizontal(get(), is_flip_horizontal ? 1 : 0);

        int rotate_degrees = 0;
        lib_player_.SmartPlayerSetRotation(get(), rotate_degrees);

        int curAudioVolume = 100;
        lib_player_.SmartPlayerSetAudioVolume(get(), curAudioVolume);
    }

    class EventHandleV2 implements NTSmartEventCallbackV2 {
        @Override
        public void onNTSmartEventCallbackV2(long handle, int id, long param1,
                                             long param2, String param3, String param4, Object param5) {

            if(event_listener_.get() != null)
            {
                event_listener_.get().onPlayerEventCallback(handle, id, param1, param2, param3, param4, param5);
            }
        }
    }

    public boolean SetMute(boolean is_mute) {
        if (!check_native_handle())
            return false;

        return OK == lib_player_.SmartPlayerSetMute(get(), is_mute? 1 : 0);
    }

    public boolean SetInputAudioVolume(int volume) {
        if (!check_native_handle())
            return false;

        return OK == lib_player_.SmartPlayerSetAudioVolume(get(), volume);
    }

    public boolean CaptureImage(int compress_format, int quality, String file_name, String user_data_string) {
        if (!check_native_handle())
            return false;

        return OK == lib_player_.CaptureImage(get(), compress_format, quality, file_name, user_data_string);
    }

    public boolean StartPlayer(boolean is_hardware_decoder, boolean is_enable_hardware_render_mode, boolean is_mute) {
        if (is_playing()) {
            Log.e(TAG, "already playing, native_handle:" + get());
            return false;
        }

        SetPlayerParam(is_hardware_decoder, is_enable_hardware_render_mode, is_mute);

        int ret = lib_player_.SmartPlayerStartPlay(get());
        if (ret != OK) {
            Log.e(TAG, "call StartPlay failed, native_handle:" + get() + ", ret:" + ret);
            return false;
        }

        write_lock_.lock();
        try {
            this.is_playing_ = true;
        } finally {
            write_lock_.unlock();
        }

        Log.i(TAG, "call StartPlayer OK, native_handle:" + get());
        return true;
    }

    public boolean StopPlayer() {
        if (!check_native_handle())
            return false;

        if (!is_playing()) {
            Log.w(TAG, "it's not playing, native_handle:" + get());
            return false;
        }

        boolean is_need_call = false;
        write_lock_.lock();
        try {
            if (this.is_playing_) {
                this.is_playing_ = false;
                is_need_call = true;
            }
        } finally {
            write_lock_.unlock();
        }

        if (is_need_call)
            lib_player_.SmartPlayerStopPlay(get());

        return true;
    }

    public boolean ConfigRecorderParam(String rec_dir, int file_max_size, int is_transcode_aac,
                                       int is_record_video, int is_record_audio) {

        if(!check_native_handle())
            return false;

        if (null == rec_dir || rec_dir.isEmpty())
            return false;

        int ret = lib_player_.SmartPlayerCreateFileDirectory(rec_dir);
        if (ret != 0) {
            Log.e(TAG, "Create record dir failed, path:" + rec_dir);
            return false;
        }

        if (lib_player_.SmartPlayerSetRecorderDirectory(get(), rec_dir) != 0) {
            Log.e(TAG, "Set record dir failed , path:" + rec_dir);
            return false;
        }

        if (lib_player_.SmartPlayerSetRecorderFileMaxSize(get(),file_max_size) != 0) {
            Log.e(TAG, "SmartPlayerSetRecorderFileMaxSize failed.");
            return false;
        }

        lib_player_.SmartPlayerSetRecorderAudioTranscodeAAC(get(), is_transcode_aac);

        // Более детальный контроль записи, Обычно нет необходимости звонить
        lib_player_.SmartPlayerSetRecorderVideo(get(), is_record_video);
        lib_player_.SmartPlayerSetRecorderAudio(get(), is_record_audio);
        return true;
    }

    public boolean StartRecorder() {

        if (is_recording()) {
            Log.e(TAG, "already recording, native_handle:" + get());
            return false;
        }

        int ret = lib_player_.SmartPlayerStartRecorder(get());
        if (ret != OK) {
            Log.e(TAG, "call SmartPlayerStartRecorder failed, native_handle:" + get() + ", ret:" + ret);
            return false;
        }

        write_lock_.lock();
        try {
            this.is_recording_ = true;
        } finally {
            write_lock_.unlock();
        }

        Log.i(TAG, "call SmartPlayerStartRecorder OK, native_handle:" + get());
        return true;
    }

    public boolean StopRecorder() {
        if (!check_native_handle())
            return false;

        if (!is_recording()) {
            Log.w(TAG, "it's not recording, native_handle:" + get());
            return false;
        }

        boolean is_need_call = false;
        write_lock_.lock();
        try {
            if (this.is_recording_) {
                this.is_recording_ = false;
                is_need_call = true;
            }
        } finally {
            write_lock_.unlock();
        }

        if (is_need_call)
            lib_player_.SmartPlayerStopRecorder(get());

        return true;
    }

    private static boolean is_null_or_empty(String val) {
        return null == val || val.isEmpty();
    }
}

Подвести итог

Выше приведены инструкции по стыковке модулей прямых трансляций RTSP и RTMP на платформе Android.,до этого,Мы провели некоторые технические обсуждения SmartPlayer.,Из различных аспектов, таких как низкая задержка, обработка синхронизации аудио и видео, многоэкземплярная реализация, эффективность декодирования, занятость производительности, стыковка декодированных данных, запись видео, обработка сетевого дрожания и т. д.,Осуществили соответствующий обмен технологиями. Заинтересованные разработчики,Вы можете обсудить это с нами индивидуально.

boy illustration
Углубленный анализ переполнения памяти CUDA: OutOfMemoryError: CUDA не хватает памяти. Попыталась выделить 3,21 Ги Б (GPU 0; всего 8,00 Ги Б).
boy illustration
[Решено] ошибка установки conda. Среда решения: не удалось выполнить первоначальное зависание. Повторная попытка с помощью файла (графическое руководство).
boy illustration
Прочитайте нейросетевую модель Трансформера в одной статье
boy illustration
.ART Теплые зимние предложения уже открыты
boy illustration
Сравнительная таблица описания кодов ошибок Amap
boy illustration
Уведомление о последних правилах Points Mall в декабре 2022 года.
boy illustration
Даже новички могут быстро приступить к работе с легким сервером приложений.
boy illustration
Взгляд на RSAC 2024|Защита конфиденциальности в эпоху больших моделей
boy illustration
Вы используете ИИ каждый день и до сих пор не знаете, как ИИ дает обратную связь? Одна статья для понимания реализации в коде Python общих функций потерь генеративных моделей + анализ принципов расчета.
boy illustration
Используйте (внутренний) почтовый ящик для образовательных учреждений, чтобы использовать Microsoft Family Bucket (1T дискового пространства на одном диске и версию Office 365 для образовательных учреждений)
boy illustration
Руководство по началу работы с оперативным проектом (7) Практическое сочетание оперативного письма — оперативного письма на основе интеллектуальной системы вопросов и ответов службы поддержки клиентов
boy illustration
[docker] Версия сервера «Чтение 3» — создайте свою собственную программу чтения веб-текста
boy illustration
Обзор Cloud-init и этапы создания в рамках PVE
boy illustration
Корпоративные пользователи используют пакет регистрационных ресурсов для регистрации ICP для веб-сайта и активации оплаты WeChat H5 (с кодом платежного узла версии API V3)
boy illustration
Подробное объяснение таких показателей производительности с высоким уровнем параллелизма, как QPS, TPS, RT и пропускная способность.
boy illustration
Удачи в конкурсе Python Essay Challenge, станьте первым, кто испытает новую функцию сообщества [Запускать блоки кода онлайн] и выиграйте множество изысканных подарков!
boy illustration
[Техническая посадка травы] Кровавая рвота и отделка позволяют вам необычным образом ощипывать гусиные перья! Не распространяйте информацию! ! !
boy illustration
[Официальное ограниченное по времени мероприятие] Сейчас ноябрь, напишите и получите приз
boy illustration
Прочтите это в одной статье: Учебник для няни по созданию сервера Huanshou Parlu на базе CVM-сервера.
boy illustration
Cloud Native | Что такое CRD (настраиваемые определения ресурсов) в K8s?
boy illustration
Как использовать Cloudflare CDN для настройки узла (CF самостоятельно выбирает IP) Гонконг, Китай/Азия узел/сводка и рекомендации внутреннего высокоскоростного IP-сегмента
boy illustration
Дополнительные правила вознаграждения амбассадоров акции в марте 2023 г.
boy illustration
Можно ли открыть частный сервер Phantom Beast Palu одним щелчком мыши? Супер простой урок для начинающих! (Прилагается метод обновления сервера)
boy illustration
[Играйте с Phantom Beast Palu] Обновите игровой сервер Phantom Beast Pallu одним щелчком мыши
boy illustration
Maotouhu делится: последний доступный внутри страны адрес склада исходного образа Docker 2024 года (обновлено 1 декабря)
boy illustration
Кодирование Base64 в MultipartFile
boy illustration
5 точек расширения SpringBoot, супер практично!
boy illustration
Глубокое понимание сопоставления индексов Elasticsearch.
boy illustration
15 рекомендуемых платформ разработки с нулевым кодом корпоративного уровня. Всегда найдется та, которая вам понравится.
boy illustration
Аннотация EasyExcel позволяет экспортировать с сохранением двух десятичных знаков.