Android Media Player RTSP

Я пытаюсь смотреть потоковую передачу RTSP на устройстве Android. Но если сервер установил пароль для потоковой передачи, мой Android не может ее воспроизвести. Если без пароля, то нормально. Вот мои коды.

public class VideoFragment extends Fragment implements View.OnClickListener, SurfaceHolder.Callback, MediaPlayer.OnPreparedListener {


VideoView m_videoView;
SurfaceView m_surfaceView;
SurfaceHolder m_surfaceHolder;
MediaPlayer m_mediaPlayer;
SharedPreferences m_sharedPref;
Boolean m_videoStarted;

public VideoFragment() {
    // Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_video, container, false);
    m_sharedPref = this.getActivity().getSharedPreferences(MainActivity.class.getSimpleName(), Context.MODE_PRIVATE);
    m_surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView);
    m_surfaceHolder = m_surfaceView.getHolder();
    m_surfaceHolder.addCallback(this);
    m_surfaceHolder.setFixedSize(320, 240);
    m_videoStarted = false;
    ToggleButton togglePlay = (ToggleButton) view.findViewById(R.id.toggleVideo);
    togglePlay.setOnClickListener(this);
    return view;
}


@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.toggleVideo:
            if(m_videoStarted == false) {
                play();
                m_videoStarted = true;
            }
            else {
                m_mediaPlayer.reset();
            }

            break;
        default:
            Log.i(AppConfig.TAG, "DEFAULT");
            break;
    }
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.e(AppConfig.TAG, "surfaceCreated");

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    m_mediaPlayer.release();
}

@Override
public void onPrepared(MediaPlayer mp) {
    Log.e(AppConfig.TAG, "Media Player Prepared");
    m_mediaPlayer.start();
}

private void play() {
    if(m_mediaPlayer == null) {
        m_mediaPlayer = new MediaPlayer();
    }
    setErrorListener();
    m_mediaPlayer.setDisplay(m_surfaceHolder);
    m_mediaPlayer.setOnPreparedListener(this);
    try {
        Map<String, String> headers =  createHeaders();
        String videoUri = "rtsp://192.168.1.100:554/video/3gpp";
        Log.i(AppConfig.TAG, "Video URI: " + videoUri);
        m_mediaPlayer.setDataSource(getActivity(), Uri.parse(videoUri), headers);
        m_mediaPlayer.prepareAsync();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void setErrorListener() {
    m_mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            if(extra == MediaPlayer.MEDIA_ERROR_IO) {
                Log.e(AppConfig.TAG, "MEDIA ERROR");
            }
            else if(extra == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
                Log.e(AppConfig.TAG, "SERVER DIED ERROR");
            }
            else if(extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
                Log.e(AppConfig.TAG, "MEDIA UNSUPPORTED");
            }
            else if(extra == MediaPlayer.MEDIA_ERROR_UNKNOWN) {
                Log.e(AppConfig.TAG, "MEDIA ERROR UNKOWN");
            }
            else if(extra == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
                Log.e(AppConfig.TAG, "NOT VALID PROGRESSIVE PLAYBACK");
            }
            else {
                Log.e(AppConfig.TAG, String.valueOf(what));
                Log.e(AppConfig.TAG, String.valueOf(extra));
                Log.e(AppConfig.TAG, "ERROR UNKNOWN!");
            }
            return false;
        }
    });
}

private Map<String, String> createHeaders() {
    String videoUri = "rtsp://192.168.1.100:554/video/3gpp";

    Map<String, String> headers =  new HashMap<String, String>();
    String camUser = "guest";//m_sharedPref.getString(AppConfig.CAM_USER,"");
    String camPassword = "tseug";//m_sharedPref.getString(AppConfig.CAM_PASSWORD,"");
    String describe = "DESCRIBE " + videoUri + " RTSP/1.0";
    String accept = "application/sdp";
    String basicAuthValue = "";

    if (camUser != "") {
        String credentials = camUser + ":" + camPassword;
        byte[] bytes = credentials.getBytes();
        int flags = Base64.URL_SAFE|Base64.NO_WRAP;
        basicAuthValue = "Basic " + Base64.encodeToString(bytes, flags);
        headers.put("Authorization", basicAuthValue);
    }

    headers.put("Request", describe);
    headers.put("Accept", accept);
    Log.i(AppConfig.TAG, "Describe: " + describe);
    Log.i(AppConfig.TAG, "Authorization: " + basicAuthValue);
    Log.i(AppConfig.TAG, "Accept: " + accept);
    return headers;
}

}

Я использую IP-камеру D-link DCS 942L. Я всегда получаю ответ 401 от камеры, но мое имя пользователя и пароль верны. Вот лог у меня получился

    05-25 21:45:31.640   1917-19687/? W/ARTSPConnection﹕ RTSP Response: 401
05-25 21:45:31.640   1917-19687/? I/MyHandler﹕ DESCRIBE completed with result 0 (Success)
05-25 21:45:31.640   1917-19687/? E/MyHandler﹕ Server responses [401] ERROR for the DESCRIBE request
05-25 21:45:31.645   1917-19687/? W/ARTSPConnection﹕ onReceiveResponse >>> State is not CONNECTED !!!
05-25 21:45:31.665   1917-19685/? V/NuPlayer﹕ scanning sources haveAudio=0, haveVideo=0
05-25 21:45:31.665   1917-19685/? V/MediaPlayerService﹕ [152] notify (0x41c61ca0, 100, 1, -2147483648)
05-25 21:45:31.665  19534-19547/com.cameraalert.app V/MediaPlayer﹕ message received msg=100, ext1=1, ext2=-2147483648
05-25 21:45:31.665  19534-19547/com.cameraalert.app E/MediaPlayer﹕ error (1, -2147483648)
05-25 21:45:31.665  19534-19547/com.cameraalert.app V/MediaPlayer﹕ callback application
05-25 21:45:31.665  19534-19547/com.cameraalert.app V/MediaPlayer﹕ back from callback
05-25 21:45:31.665  19534-19534/com.cameraalert.app E/MediaPlayer﹕ Error (1,-2147483648)
05-25 21:45:31.665  19534-19534/com.cameraalert.app E/CameraAlert﹕ 1
05-25 21:45:31.665  19534-19534/com.cameraalert.app E/CameraAlert﹕ -2147483648
05-25 21:45:31.665  19534-19534/com.cameraalert.app E/CameraAlert﹕ ERROR UNKNOWN!

Интересно, неправильный ли заголовок моего запроса. Здесь есть статья http://www.tuicool.com/articles/R7ZF7bF о том, как играть в RTSP с аутентификацией, используя ту же камеру, что и я. Я буду очень благодарен за любую помощь или объяснение.


person dev0x10    schedule 25.05.2014    source источник
comment
При всем уважении, пробовали ли вы "rtsp://guest:[email protected]:554/video/3gpp" без рукопожатия аутентификации?   -  person Alex Cohn    schedule 25.05.2014
comment
@AlexCohn Да, с тем же результатом   -  person dev0x10    schedule 26.05.2014


Ответы (1)


Я разрабатываю код, связанный с RTSP, как и вы, и пришел к следующим выводам:

  1. При использовании MediaPlayer ваш RTSP_URL может выглядеть так: rtsp://account:[email protected]
  2. Но с упомянутой вами аутентификацией, если ваша камера использует базовую аутентификацию HTTP, вам нужно добавить заголовок в http-запрос:, например (я использую OKHTTP 2.0):

    String basicAuth = Credentials.basic("учетная запись", "пропуск"); Запрос запроса = новый Request.Builder().url(url).header("Авторизация", basicAuth).build(); Ответный ответ = client.newCall(request).execute();

Это подходит для меня! Надеюсь, это будет полезно для вас.

person Charlie MAI    schedule 22.08.2014
comment
Я уже добавил заголовок для учетных данных (посмотрите на код), или это неправильный способ добавления заголовка? - person dev0x10; 04.11.2014
comment
Я пытаюсь заставить MediaPlayer работать с аутентификацией rtsp Digest, подделывая свои собственные заголовки. Я знал, что у меня правильные заголовки, потому что я захватил пакет с помощью tPacketCapture, и ipcam ответил мне 200 OK. но когда я передаю поддельные заголовки (например, ArrayMap<String, String> headers = new ArrayMap<>(1); headers.add("Authorization", correctDigestAuth); в setDataSource(Context, Uri, Map‹String, String›), он просто не отправляет мои пользовательские заголовки вообще (проверено с помощью tPacketCapture). Есть ли способ заставить его отправлять заголовки, чтобы остальная часть сеанса аутентифицировалась? - person 曾其威; 25.02.2015